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/execute.hpp"
39 #include "utils/c-array.hpp"
42 #include <lxc/lxccontainer.h>
45 #include <utils/initctl.hpp>
54 #define ITEM(X) {#X, LxcZone::State::X},
55 const std::map<std::string, LxcZone::State> STATE_MAP = {
68 std::string LxcZone::toString(State state)
70 #define CASE(X) case LxcZone::State::X: return #X;
82 throw LxcException("Invalid state");
85 LxcZone::LxcZone(const std::string& lxcPath, const std::string& zoneName)
86 : mLxcContainer(nullptr)
88 mLxcContainer = lxc_container_new(zoneName.c_str(), lxcPath.c_str());
90 LOGE("Could not initialize lxc zone " << zoneName << " in path " << lxcPath);
91 throw LxcException("Could not initialize lxc zone");
97 lxc_container_put(mLxcContainer);
100 std::string LxcZone::getName() const
102 return mLxcContainer->name;
105 std::string LxcZone::getConfigItem(const std::string& key)
108 int len = mLxcContainer->get_config_item(mLxcContainer, key.c_str(), buffer, sizeof(buffer));
110 LOGE("Key '" << key << "' not found in zone " << getName());
111 throw LxcException("Key not found");
116 bool LxcZone::isDefined()
118 return mLxcContainer->is_defined(mLxcContainer);
121 LxcZone::State LxcZone::getState()
123 const std::string str = mLxcContainer->state(mLxcContainer);
124 return STATE_MAP.at(str);
127 bool LxcZone::create(const std::string& templatePath, const char* const* argv)
130 utils::CStringArrayBuilder args;
131 args.add("lxc-create")
132 .add("-n").add(mLxcContainer->name)
133 .add("-t").add(templatePath.c_str())
134 .add("-P").add(mLxcContainer->config_path);
144 if (!utils::executeAndWait("/usr/bin/lxc-create", args.c_array())) {
145 LOGE("Could not create zone " << getName());
152 if (!mLxcContainer->create(mLxcContainer,
153 templatePath.c_str(),
155 const_cast<char* const*>(argv))) {
156 LOGE("Could not create zone " << getName());
163 bool LxcZone::destroy()
165 if (!mLxcContainer->destroy(mLxcContainer)) {
166 LOGE("Could not destroy zone " << getName());
172 bool LxcZone::start(const char* const* argv)
175 if (mLxcContainer->is_running(mLxcContainer)) {
176 LOGE("Already started " << getName());
180 utils::CStringArrayBuilder args;
181 args.add("lxc-start")
183 .add("-n").add(mLxcContainer->name)
184 .add("-P").add(mLxcContainer->config_path);
194 if (!utils::executeAndWait("/usr/bin/lxc-start", args.c_array())) {
195 LOGE("Could not start zone " << getName());
201 // we have to check status because lxc-start runs in daemonized mode
202 if (!mLxcContainer->is_running(mLxcContainer)) {
203 LOGE("Could not start init in zone " << getName());
208 if (mLxcContainer->is_running(mLxcContainer)) {
209 LOGE("Already started " << getName());
212 if (!mLxcContainer->want_daemonize(mLxcContainer, true)) {
213 LOGE("Could not configure zone " << getName());
216 if (!mLxcContainer->start(mLxcContainer, false, const_cast<char* const*>(argv))) {
217 LOGE("Could not start zone " << getName());
226 if (!mLxcContainer->stop(mLxcContainer)) {
227 LOGE("Could not stop zone " << getName());
233 bool LxcZone::reboot()
235 if (!mLxcContainer->reboot(mLxcContainer)) {
236 LOGE("Could not reboot zone " << getName());
242 bool LxcZone::shutdown(int timeout)
244 State state = getState();
245 if (state == State::STOPPED) {
248 if (state != State::RUNNING) {
249 LOGE("Could not gracefully shutdown zone " << getName());
254 utils::CStringArrayBuilder args;
255 std::string timeoutStr = std::to_string(timeout);
257 .add("-n").add(mLxcContainer->name)
258 .add("-P").add(mLxcContainer->config_path)
259 .add("-t").add(timeoutStr.c_str())
262 if (!utils::executeAndWait("/usr/bin/lxc-stop", args.c_array())) {
263 LOGE("Could not gracefully shutdown zone " << getName() << " in " << timeout << "s");
270 // try shutdown by sending poweroff to init
271 if (setRunLevel(utils::RUNLEVEL_POWEROFF)) {
272 if (!waitForState(State::STOPPED, timeout)) {
273 LOGE("Could not gracefully shutdown zone " << getName() << " in " << timeout << "s");
278 LOGW("SetRunLevel failed for zone " + getName());
280 // fallback for other inits like bash: lxc sends 'lxc.haltsignal' signal to init
281 if (!mLxcContainer->shutdown(mLxcContainer, timeout)) {
282 LOGE("Could not gracefully shutdown zone " << getName() << " in " << timeout << "s");
289 bool LxcZone::freeze()
291 if (!mLxcContainer->freeze(mLxcContainer)) {
292 LOGE("Could not freeze zone " << getName());
298 bool LxcZone::unfreeze()
300 if (!mLxcContainer->unfreeze(mLxcContainer)) {
301 LOGE("Could not unfreeze zone " << getName());
307 bool LxcZone::waitForState(State state, int timeout)
309 if (!mLxcContainer->wait(mLxcContainer, toString(state).c_str(), timeout)) {
310 LOGD("Timeout while waiting for state " << toString(state) << " of zone " << getName());
316 pid_t LxcZone::getInitPid() const
318 return mLxcContainer->init_pid(mLxcContainer);
321 bool LxcZone::setRunLevel(int runLevel)
323 auto callback = [](void* param) -> int {
324 utils::RunLevel level = *reinterpret_cast<utils::RunLevel*>(param);
325 return utils::setRunLevel(level) ? 0 : 1;
328 lxc_attach_options_t options = LXC_ATTACH_OPTIONS_DEFAULT;
330 int ret = mLxcContainer->attach(mLxcContainer, callback, &runLevel, &options, &pid);
335 if (!utils::waitPid(pid, status)) {
341 void LxcZone::refresh()
343 //TODO Consider make LxcZone state-less
344 std::string zoneName = mLxcContainer->name;
345 std::string lxcPath = mLxcContainer->config_path;
346 lxc_container_put(mLxcContainer);
347 mLxcContainer = lxc_container_new(zoneName.c_str(), lxcPath.c_str());