Creating netdev
[platform/core/security/vasum.git] / common / lxc / zone.cpp
1 /*
2  *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
5  *
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
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
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
17  */
18
19 /**
20  * @file
21  * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
22  * @brief   Lxc zone
23  */
24
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)
32
33 #include "config.hpp"
34 #include "logger/logger.hpp"
35 #include "lxc/zone.hpp"
36 #include "lxc/exception.hpp"
37 #include "utils/execute.hpp"
38 #ifdef USE_EXEC
39 #include "utils/c-array.hpp"
40 #endif
41
42 #include <lxc/lxccontainer.h>
43 #include <sys/stat.h>
44
45 #include <utils/initctl.hpp>
46 #include <sys/wait.h>
47
48 #include <map>
49
50 namespace vasum {
51 namespace lxc {
52
53 namespace {
54 #define ITEM(X) {#X, LxcZone::State::X},
55     const std::map<std::string, LxcZone::State> STATE_MAP = {
56         ITEM(STOPPED)
57         ITEM(STARTING)
58         ITEM(RUNNING)
59         ITEM(STOPPING)
60         ITEM(ABORTING)
61         ITEM(FREEZING)
62         ITEM(FROZEN)
63         ITEM(THAWED)
64     };
65 #undef ITEM
66 } // namespace
67
68 std::string LxcZone::toString(State state)
69 {
70 #define CASE(X) case LxcZone::State::X: return #X;
71     switch (state) {
72     CASE(STOPPED)
73     CASE(STARTING)
74     CASE(RUNNING)
75     CASE(STOPPING)
76     CASE(ABORTING)
77     CASE(FREEZING)
78     CASE(FROZEN)
79     CASE(THAWED)
80     }
81 #undef CASE
82     throw LxcException("Invalid state");
83 }
84
85 LxcZone::LxcZone(const std::string& lxcPath, const std::string& zoneName)
86   : mLxcContainer(nullptr)
87 {
88     mLxcContainer = lxc_container_new(zoneName.c_str(), lxcPath.c_str());
89     if (!mLxcContainer) {
90         LOGE("Could not initialize lxc zone " << zoneName << " in path " << lxcPath);
91         throw LxcException("Could not initialize lxc zone");
92     }
93 }
94
95 LxcZone::~LxcZone()
96 {
97     lxc_container_put(mLxcContainer);
98 }
99
100 std::string LxcZone::getName() const
101 {
102     return mLxcContainer->name;
103 }
104
105 std::string LxcZone::getConfigItem(const std::string& key)
106 {
107     char buffer[1024];
108     int len = mLxcContainer->get_config_item(mLxcContainer, key.c_str(), buffer, sizeof(buffer));
109     if (len < 0) {
110         LOGE("Key '" << key << "' not found in zone " << getName());
111         throw LxcException("Key not found");
112     }
113     return buffer;
114 }
115
116 bool LxcZone::isDefined()
117 {
118     return mLxcContainer->is_defined(mLxcContainer);
119 }
120
121 LxcZone::State LxcZone::getState()
122 {
123     const std::string str = mLxcContainer->state(mLxcContainer);
124     return STATE_MAP.at(str);
125 }
126
127 bool LxcZone::create(const std::string& templatePath, const char* const* argv)
128 {
129 #ifdef USE_EXEC
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);
135
136     if (*argv) {
137         args.add("--");
138     }
139
140     while (*argv) {
141         args.add(*argv++);
142     }
143
144     if (!utils::executeAndWait("/usr/bin/lxc-create", args.c_array())) {
145         LOGE("Could not create zone " << getName());
146         return false;
147     }
148
149     refresh();
150     return true;
151 #else
152     if (!mLxcContainer->create(mLxcContainer,
153                                templatePath.c_str(),
154                                NULL, NULL, 0,
155                                const_cast<char* const*>(argv))) {
156         LOGE("Could not create zone " << getName());
157         return false;
158     }
159     return true;
160 #endif
161 }
162
163 bool LxcZone::destroy()
164 {
165     if (!mLxcContainer->destroy(mLxcContainer)) {
166         LOGE("Could not destroy zone " << getName());
167         return false;
168     }
169     return true;
170 }
171
172 bool LxcZone::start(const char* const* argv)
173 {
174 #ifdef USE_EXEC
175     if (mLxcContainer->is_running(mLxcContainer)) {
176         LOGE("Already started " << getName());
177         return false;
178     }
179
180     utils::CStringArrayBuilder args;
181     args.add("lxc-start")
182         .add("-d")
183         .add("-n").add(mLxcContainer->name)
184         .add("-P").add(mLxcContainer->config_path);
185
186     if (*argv) {
187         args.add("--");
188     }
189
190     while (*argv) {
191         args.add(*argv++);
192     }
193
194     if (!utils::executeAndWait("/usr/bin/lxc-start", args.c_array())) {
195         LOGE("Could not start zone " << getName());
196         return false;
197     }
198
199     refresh();
200
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());
204         return false;
205     }
206     return true;
207 #else
208     if (mLxcContainer->is_running(mLxcContainer)) {
209         LOGE("Already started " << getName());
210         return false;
211     }
212     if (!mLxcContainer->want_daemonize(mLxcContainer, true)) {
213         LOGE("Could not configure zone " << getName());
214         return false;
215     }
216     if (!mLxcContainer->start(mLxcContainer, false, const_cast<char* const*>(argv))) {
217         LOGE("Could not start zone " << getName());
218         return false;
219     }
220     return true;
221 #endif
222 }
223
224 bool LxcZone::stop()
225 {
226     if (!mLxcContainer->stop(mLxcContainer)) {
227         LOGE("Could not stop zone " << getName());
228         return false;
229     }
230     return true;
231 }
232
233 bool LxcZone::reboot()
234 {
235     if (!mLxcContainer->reboot(mLxcContainer)) {
236         LOGE("Could not reboot zone " << getName());
237         return false;
238     }
239     return true;
240 }
241
242 bool LxcZone::shutdown(int timeout)
243 {
244     State state = getState();
245     if (state == State::STOPPED) {
246         return true;
247     }
248     if (state != State::RUNNING) {
249         LOGE("Could not gracefully shutdown zone " << getName());
250         return false;
251     }
252
253 #ifdef USE_EXEC
254     utils::CStringArrayBuilder args;
255     std::string timeoutStr = std::to_string(timeout);
256     args.add("lxc-stop")
257         .add("-n").add(mLxcContainer->name)
258         .add("-P").add(mLxcContainer->config_path)
259         .add("-t").add(timeoutStr.c_str())
260         .add("--nokill");
261
262     if (!utils::executeAndWait("/usr/bin/lxc-stop", args.c_array())) {
263         LOGE("Could not gracefully shutdown zone " << getName() << " in " << timeout << "s");
264         return false;
265     }
266
267     refresh();
268     return true;
269 #else
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");
274             return false;
275         }
276         return true;
277     }
278     LOGW("SetRunLevel failed for zone " + getName());
279
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");
283         return false;
284     }
285     return true;
286 #endif
287 }
288
289 bool LxcZone::freeze()
290 {
291     if (!mLxcContainer->freeze(mLxcContainer)) {
292         LOGE("Could not freeze zone " << getName());
293         return false;
294     }
295     return true;
296 }
297
298 bool LxcZone::unfreeze()
299 {
300     if (!mLxcContainer->unfreeze(mLxcContainer)) {
301         LOGE("Could not unfreeze zone " << getName());
302         return false;
303     }
304     return true;
305 }
306
307 bool LxcZone::waitForState(State state, int timeout)
308 {
309     if (!mLxcContainer->wait(mLxcContainer, toString(state).c_str(), timeout)) {
310         LOGD("Timeout while waiting for state " << toString(state) << " of zone " << getName());
311         return false;
312     }
313     return true;
314 }
315
316 pid_t LxcZone::getInitPid() const
317 {
318     return mLxcContainer->init_pid(mLxcContainer);
319 }
320
321 bool LxcZone::setRunLevel(int runLevel)
322 {
323     auto callback = [](void* param) -> int {
324         utils::RunLevel level = *reinterpret_cast<utils::RunLevel*>(param);
325         return utils::setRunLevel(level) ? 0 : 1;
326     };
327
328     lxc_attach_options_t options = LXC_ATTACH_OPTIONS_DEFAULT;
329     pid_t pid;
330     int ret = mLxcContainer->attach(mLxcContainer, callback, &runLevel, &options, &pid);
331     if (ret != 0) {
332         return false;
333     }
334     int status;
335     if (!utils::waitPid(pid, status)) {
336         return false;
337     }
338     return status == 0;
339 }
340
341 void LxcZone::refresh()
342 {
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());
348 }
349
350
351 } // namespace lxc
352 } // namespace vasum