Adjust to coding style rules
[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/exception.hpp"
38 #include "utils/execute.hpp"
39 #include "utils/fd-utils.hpp"
40 #ifdef USE_EXEC
41 #include "utils/c-array.hpp"
42 #endif
43 #include "utils/initctl.hpp"
44
45 #include <lxc/lxccontainer.h>
46 #include <sys/stat.h>
47 #include <sys/wait.h>
48
49 #include <map>
50 #include <sys/socket.h>
51 #include <fcntl.h>
52 #include <unistd.h>
53
54 namespace vasum {
55 namespace lxc {
56
57 namespace {
58 #define ITEM(X) {#X, LxcZone::State::X},
59 const std::map<std::string, LxcZone::State> STATE_MAP = {
60     ITEM(STOPPED)
61     ITEM(STARTING)
62     ITEM(RUNNING)
63     ITEM(STOPPING)
64     ITEM(ABORTING)
65     ITEM(FREEZING)
66     ITEM(FROZEN)
67     ITEM(THAWED)
68 };
69 #undef ITEM
70
71 int execFunction(void* data) {
72     // Executed by C code, so catch all exceptions
73     try {
74         return (*static_cast<std::function<int()>*>(data))();
75     } catch(...) {
76         return -1; // Non-zero on failure
77     }
78     return 0; // Success
79 }
80
81 } // namespace
82
83 std::string LxcZone::toString(State state) {
84 #define CASE(X) case LxcZone::State::X: return #X;
85     switch (state) {
86         CASE(STOPPED)
87         CASE(STARTING)
88         CASE(RUNNING)
89         CASE(STOPPING)
90         CASE(ABORTING)
91         CASE(FREEZING)
92         CASE(FROZEN)
93         CASE(THAWED)
94     }
95 #undef CASE
96     throw LxcException("Invalid state");
97 }
98
99 LxcZone::LxcZone(const std::string& lxcPath, const std::string& zoneName)
100     : mLxcContainer(nullptr)
101 {
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");
106     }
107 }
108
109 LxcZone::~LxcZone()
110 {
111     lxc_container_put(mLxcContainer);
112 }
113
114 std::string LxcZone::getName() const
115 {
116     return mLxcContainer->name;
117 }
118
119 std::string LxcZone::getConfigItem(const std::string& key)
120 {
121     char buffer[1024];
122     int len = mLxcContainer->get_config_item(mLxcContainer, key.c_str(), buffer, sizeof(buffer));
123     if (len < 0) {
124         LOGE("Key '" << key << "' not found in zone " << getName());
125         throw LxcException("Key not found");
126     }
127     return buffer;
128 }
129
130 bool LxcZone::isDefined()
131 {
132     return mLxcContainer->is_defined(mLxcContainer);
133 }
134
135 LxcZone::State LxcZone::getState()
136 {
137     const std::string str = mLxcContainer->state(mLxcContainer);
138     return STATE_MAP.at(str);
139 }
140
141 bool LxcZone::create(const std::string& templatePath, const char* const* argv)
142 {
143 #ifdef USE_EXEC
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);
149
150     if (*argv) {
151         args.add("--");
152     }
153
154     while (*argv) {
155         args.add(*argv++);
156     }
157
158     if (!utils::executeAndWait("/usr/bin/lxc-create", args.c_array())) {
159         LOGE("Could not create zone " << getName());
160         return false;
161     }
162
163     refresh();
164     return true;
165 #else
166     if (!mLxcContainer->create(mLxcContainer,
167                                templatePath.c_str(),
168                                NULL, NULL, 0,
169                                const_cast<char* const*>(argv))) {
170         LOGE("Could not create zone " << getName());
171         return false;
172     }
173     return true;
174 #endif
175 }
176
177 bool LxcZone::destroy()
178 {
179     if (!mLxcContainer->destroy(mLxcContainer)) {
180         LOGE("Could not destroy zone " << getName());
181         return false;
182     }
183     return true;
184 }
185
186 bool LxcZone::start(const char* const* argv)
187 {
188 #ifdef USE_EXEC
189     if (mLxcContainer->is_running(mLxcContainer)) {
190         LOGE("Already started " << getName());
191         return false;
192     }
193
194     utils::CStringArrayBuilder args;
195     args.add("lxc-start")
196     .add("-d")
197     .add("-n").add(mLxcContainer->name)
198     .add("-P").add(mLxcContainer->config_path);
199
200     if (*argv) {
201         args.add("--");
202     }
203
204     while (*argv) {
205         args.add(*argv++);
206     }
207
208     if (!utils::executeAndWait("/usr/bin/lxc-start", args.c_array())) {
209         LOGE("Could not start zone " << getName());
210         return false;
211     }
212
213     refresh();
214
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());
218         return false;
219     }
220     return true;
221 #else
222     if (mLxcContainer->is_running(mLxcContainer)) {
223         LOGE("Already started " << getName());
224         return false;
225     }
226     if (!mLxcContainer->want_daemonize(mLxcContainer, true)) {
227         LOGE("Could not configure zone " << getName());
228         return false;
229     }
230     if (!mLxcContainer->start(mLxcContainer, false, const_cast<char* const*>(argv))) {
231         LOGE("Could not start zone " << getName());
232         return false;
233     }
234     return true;
235 #endif
236 }
237
238 bool LxcZone::stop()
239 {
240     if (!mLxcContainer->stop(mLxcContainer)) {
241         LOGE("Could not stop zone " << getName());
242         return false;
243     }
244     return true;
245 }
246
247 bool LxcZone::reboot()
248 {
249     if (!mLxcContainer->reboot(mLxcContainer)) {
250         LOGE("Could not reboot zone " << getName());
251         return false;
252     }
253     return true;
254 }
255
256 bool LxcZone::shutdown(int timeout)
257 {
258     State state = getState();
259     if (state == State::STOPPED) {
260         return true;
261     }
262     if (state != State::RUNNING) {
263         LOGE("Could not gracefully shutdown zone " << getName());
264         return false;
265     }
266
267 #ifdef USE_EXEC
268     utils::CStringArrayBuilder args;
269     std::string timeoutStr = std::to_string(timeout);
270     args.add("lxc-stop")
271     .add("-n").add(mLxcContainer->name)
272     .add("-P").add(mLxcContainer->config_path)
273     .add("-t").add(timeoutStr.c_str())
274     .add("--nokill");
275
276     if (!utils::executeAndWait("/usr/bin/lxc-stop", args.c_array())) {
277         LOGE("Could not gracefully shutdown zone " << getName() << " in " << timeout << "s");
278         return false;
279     }
280
281     refresh();
282     return true;
283 #else
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");
288             return false;
289         }
290         return true;
291     }
292     LOGW("SetRunLevel failed for zone " + getName());
293
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");
297         return false;
298     }
299     return true;
300 #endif
301 }
302
303 bool LxcZone::freeze()
304 {
305     if (!mLxcContainer->freeze(mLxcContainer)) {
306         LOGE("Could not freeze zone " << getName());
307         return false;
308     }
309     return true;
310 }
311
312 bool LxcZone::unfreeze()
313 {
314     if (!mLxcContainer->unfreeze(mLxcContainer)) {
315         LOGE("Could not unfreeze zone " << getName());
316         return false;
317     }
318     return true;
319 }
320
321 bool LxcZone::waitForState(State state, int timeout)
322 {
323     if (!mLxcContainer->wait(mLxcContainer, toString(state).c_str(), timeout)) {
324         LOGD("Timeout while waiting for state " << toString(state) << " of zone " << getName());
325         return false;
326     }
327     return true;
328 }
329
330 pid_t LxcZone::getInitPid() const
331 {
332     return mLxcContainer->init_pid(mLxcContainer);
333 }
334
335 bool LxcZone::setRunLevel(int runLevel)
336 {
337     Call call = [runLevel]() -> int {
338         return utils::setRunLevel(static_cast<utils::RunLevel>(runLevel)) ? 0 : 1;
339     };
340     return runInZone(call);
341 }
342
343 void LxcZone::refresh()
344 {
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());
350 }
351
352 bool LxcZone::runInZone(Call& call)
353 {
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 |
359                            LXC_ATTACH_LSM_NOW |
360                            LXC_ATTACH_MOVE_TO_CGROUP;
361
362     pid_t pid;
363     int ret = mLxcContainer->attach(mLxcContainer,
364                                     execFunction,
365                                     &call,
366                                     &options,
367                                     &pid);
368     if (ret != 0) {
369         return false;
370     }
371     int status;
372     if (!utils::waitPid(pid, status)) {
373         return false;
374     }
375     return status == 0;
376 }
377
378 bool LxcZone::createFile(const std::string& path,
379                          const std::int32_t flags,
380                          const std::int32_t mode,
381                          int *fdPtr)
382 {
383     *fdPtr = -1;
384
385     int sockets[2];
386     if (::socketpair(AF_LOCAL, SOCK_STREAM, 0, sockets) < 0) {
387         LOGE("Can't create socket pair: " << utils::getSystemErrorMessage());
388         return false;
389     }
390
391     lxc::LxcZone::Call call = [&]()->int{
392         utils::close(sockets[1]);
393
394         int fd = ::open(path.c_str(), O_CREAT | O_EXCL | flags, mode);
395         if (fd < 0) {
396             LOGE("Error during file creation: " << utils::getSystemErrorMessage());
397             utils::close(sockets[0]);
398             return -1;
399         }
400         LOGT("Created file in zone with fd " << fd);
401         utils::fdSend(sockets[0], fd);
402         utils::close(fd);
403         return 0;
404     };
405
406     runInZone(call);
407
408     utils::close(sockets[0]);
409     *fdPtr = utils::fdRecv(sockets[1]);
410     utils::close(sockets[1]);
411
412     return true;
413 }
414
415 } // namespace lxc
416 } // namespace vasum