2 * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
4 * Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License
21 * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
25 // Define macro USE_EXEC if you are goind to use valgrind
26 // TODO Consider always using exec to control lxc. Advantages:
27 // - 'ps' output is more descriptive with lxc-start
28 // - lxc library is not tested well with multithread programs
29 // (there are still some issues like using umask etc.)
30 // - it could be possible not to be uid 0 in this process
31 // - fork + unshare does not work with traced process (e.g. under valgrind)
34 #include "logger/logger.hpp"
35 #include "lxc/zone.hpp"
36 #include "lxc/exception.hpp"
37 #include "utils/exception.hpp"
38 #include "utils/execute.hpp"
39 #include "utils/fd-utils.hpp"
41 #include "utils/c-array.hpp"
43 #include "utils/initctl.hpp"
45 #include <lxc/lxccontainer.h>
50 #include <sys/socket.h>
58 #define ITEM(X) {#X, LxcZone::State::X},
59 const std::map<std::string, LxcZone::State> STATE_MAP = {
71 int execFunction(void* data) {
72 // Executed by C code, so catch all exceptions
74 return (*static_cast<std::function<int()>*>(data))();
76 return -1; // Non-zero on failure
83 std::string LxcZone::toString(State state) {
84 #define CASE(X) case LxcZone::State::X: return #X;
96 throw LxcException("Invalid state");
99 LxcZone::LxcZone(const std::string& lxcPath, const std::string& zoneName)
100 : mLxcContainer(nullptr)
102 mLxcContainer = lxc_container_new(zoneName.c_str(), lxcPath.c_str());
103 if (!mLxcContainer) {
104 LOGE("Could not initialize lxc zone " << zoneName << " in path " << lxcPath);
105 throw LxcException("Could not initialize lxc zone");
111 lxc_container_put(mLxcContainer);
114 std::string LxcZone::getName() const
116 return mLxcContainer->name;
119 std::string LxcZone::getConfigItem(const std::string& key)
122 int len = mLxcContainer->get_config_item(mLxcContainer, key.c_str(), buffer, sizeof(buffer));
124 LOGE("Key '" << key << "' not found in zone " << getName());
125 throw LxcException("Key not found");
130 bool LxcZone::isDefined()
132 return mLxcContainer->is_defined(mLxcContainer);
135 LxcZone::State LxcZone::getState()
137 const std::string str = mLxcContainer->state(mLxcContainer);
138 return STATE_MAP.at(str);
141 bool LxcZone::create(const std::string& templatePath, const char* const* argv)
144 utils::CStringArrayBuilder args;
145 args.add("lxc-create")
146 .add("-n").add(mLxcContainer->name)
147 .add("-t").add(templatePath.c_str())
148 .add("-P").add(mLxcContainer->config_path);
158 if (!utils::executeAndWait("/usr/bin/lxc-create", args.c_array())) {
159 LOGE("Could not create zone " << getName());
166 if (!mLxcContainer->create(mLxcContainer,
167 templatePath.c_str(),
169 const_cast<char* const*>(argv))) {
170 LOGE("Could not create zone " << getName());
177 bool LxcZone::destroy()
179 if (!mLxcContainer->destroy(mLxcContainer)) {
180 LOGE("Could not destroy zone " << getName());
186 bool LxcZone::start(const char* const* argv)
189 if (mLxcContainer->is_running(mLxcContainer)) {
190 LOGE("Already started " << getName());
194 utils::CStringArrayBuilder args;
195 args.add("lxc-start")
197 .add("-n").add(mLxcContainer->name)
198 .add("-P").add(mLxcContainer->config_path);
208 if (!utils::executeAndWait("/usr/bin/lxc-start", args.c_array())) {
209 LOGE("Could not start zone " << getName());
215 // we have to check status because lxc-start runs in daemonized mode
216 if (!mLxcContainer->is_running(mLxcContainer)) {
217 LOGE("Could not start init in zone " << getName());
222 if (mLxcContainer->is_running(mLxcContainer)) {
223 LOGE("Already started " << getName());
226 if (!mLxcContainer->want_daemonize(mLxcContainer, true)) {
227 LOGE("Could not configure zone " << getName());
230 if (!mLxcContainer->start(mLxcContainer, false, const_cast<char* const*>(argv))) {
231 LOGE("Could not start zone " << getName());
240 if (!mLxcContainer->stop(mLxcContainer)) {
241 LOGE("Could not stop zone " << getName());
247 bool LxcZone::reboot()
249 if (!mLxcContainer->reboot(mLxcContainer)) {
250 LOGE("Could not reboot zone " << getName());
256 bool LxcZone::shutdown(int timeout)
258 State state = getState();
259 if (state == State::STOPPED) {
262 if (state != State::RUNNING) {
263 LOGE("Could not gracefully shutdown zone " << getName());
268 utils::CStringArrayBuilder args;
269 std::string timeoutStr = std::to_string(timeout);
271 .add("-n").add(mLxcContainer->name)
272 .add("-P").add(mLxcContainer->config_path)
273 .add("-t").add(timeoutStr.c_str())
276 if (!utils::executeAndWait("/usr/bin/lxc-stop", args.c_array())) {
277 LOGE("Could not gracefully shutdown zone " << getName() << " in " << timeout << "s");
284 // try shutdown by sending poweroff to init
285 if (setRunLevel(utils::RUNLEVEL_POWEROFF)) {
286 if (!waitForState(State::STOPPED, timeout)) {
287 LOGE("Could not gracefully shutdown zone " << getName() << " in " << timeout << "s");
292 LOGW("SetRunLevel failed for zone " + getName());
294 // fallback for other inits like bash: lxc sends 'lxc.haltsignal' signal to init
295 if (!mLxcContainer->shutdown(mLxcContainer, timeout)) {
296 LOGE("Could not gracefully shutdown zone " << getName() << " in " << timeout << "s");
303 bool LxcZone::freeze()
305 if (!mLxcContainer->freeze(mLxcContainer)) {
306 LOGE("Could not freeze zone " << getName());
312 bool LxcZone::unfreeze()
314 if (!mLxcContainer->unfreeze(mLxcContainer)) {
315 LOGE("Could not unfreeze zone " << getName());
321 bool LxcZone::waitForState(State state, int timeout)
323 if (!mLxcContainer->wait(mLxcContainer, toString(state).c_str(), timeout)) {
324 LOGD("Timeout while waiting for state " << toString(state) << " of zone " << getName());
330 pid_t LxcZone::getInitPid() const
332 return mLxcContainer->init_pid(mLxcContainer);
335 bool LxcZone::setRunLevel(int runLevel)
337 Call call = [runLevel]() -> int {
338 return utils::setRunLevel(static_cast<utils::RunLevel>(runLevel)) ? 0 : 1;
340 return runInZone(call);
343 void LxcZone::refresh()
345 //TODO Consider make LxcZone state-less
346 std::string zoneName = mLxcContainer->name;
347 std::string lxcPath = mLxcContainer->config_path;
348 lxc_container_put(mLxcContainer);
349 mLxcContainer = lxc_container_new(zoneName.c_str(), lxcPath.c_str());
352 bool LxcZone::runInZone(Call& call)
354 lxc_attach_options_t options = LXC_ATTACH_OPTIONS_DEFAULT;
355 options.attach_flags = LXC_ATTACH_REMOUNT_PROC_SYS |
356 LXC_ATTACH_DROP_CAPABILITIES |
357 LXC_ATTACH_SET_PERSONALITY |
358 LXC_ATTACH_LSM_EXEC |
360 LXC_ATTACH_MOVE_TO_CGROUP;
363 int ret = mLxcContainer->attach(mLxcContainer,
372 if (!utils::waitPid(pid, status)) {
378 bool LxcZone::createFile(const std::string& path,
379 const std::int32_t flags,
380 const std::int32_t mode,
386 if (::socketpair(AF_LOCAL, SOCK_STREAM, 0, sockets) < 0) {
387 LOGE("Can't create socket pair: " << utils::getSystemErrorMessage());
391 lxc::LxcZone::Call call = [&]()->int{
392 utils::close(sockets[1]);
394 int fd = ::open(path.c_str(), O_CREAT | O_EXCL | flags, mode);
396 LOGE("Error during file creation: " << utils::getSystemErrorMessage());
397 utils::close(sockets[0]);
400 LOGT("Created file in zone with fd " << fd);
401 utils::fdSend(sockets[0], fd);
408 utils::close(sockets[0]);
409 *fdPtr = utils::fdRecv(sockets[1]);
410 utils::close(sockets[1]);