2 * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
4 * Contact: Jan Olszak <j.olszak@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 Jan Olszak (j.olszak@samsung.com)
22 * @brief Implementation of class for administrating one container
27 #include "container-admin.hpp"
28 #include "exception.hpp"
30 #include "libvirt/helpers.hpp"
31 #include "logger/logger.hpp"
32 #include "utils/fs.hpp"
33 #include "utils/latch.hpp"
34 #include "utils/callback-wrapper.hpp"
43 namespace security_containers {
47 // TODO: this should be in container's configuration file
48 const int SHUTDOWN_WAIT = 10 * 1000;
50 std::string getDomainName(virDomainPtr dom)
54 const char* name = virDomainGetName(dom);
55 if (name == nullptr) {
56 LOGE("Failed to get the domain's id:\n"
57 << libvirt::libvirtFormatError());
58 throw ContainerOperationException();
66 const std::uint64_t DEFAULT_CPU_SHARES = 1024;
67 const std::uint64_t DEFAULT_VCPU_PERIOD_MS = 100000;
69 ContainerAdmin::ContainerAdmin(const ContainerConfig& config)
71 mDom(utils::readFileContent(mConfig.config)),
72 mId(getDomainName(mDom.get())),
74 mLifecycleCallbackId(-1),
75 mRebootCallbackId(-1),
78 LOGD(mId << ": Instantiating ContainerAdmin object");
80 // ContainerAdmin owns those callbacks
81 mLifecycleCallbackId = virConnectDomainEventRegisterAny(virDomainGetConnect(mDom.get()),
83 VIR_DOMAIN_EVENT_ID_LIFECYCLE,
84 VIR_DOMAIN_EVENT_CALLBACK(&ContainerAdmin::libvirtLifecycleCallback),
85 utils::createCallbackWrapper(this, mLibvirtGuard.spawn()),
86 &utils::deleteCallbackWrapper<ContainerAdmin*>);
88 if (mLifecycleCallbackId < 0) {
89 LOGE(mId << ": Failed to register a libvirt lifecycle callback");
90 throw ContainerOperationException(mId + ": Failed to register a libvirt lifecycle callback");
93 mRebootCallbackId = virConnectDomainEventRegisterAny(virDomainGetConnect(mDom.get()),
95 VIR_DOMAIN_EVENT_ID_REBOOT,
96 VIR_DOMAIN_EVENT_CALLBACK(&ContainerAdmin::libvirtRebootCallback),
97 utils::createCallbackWrapper(this, mLibvirtGuard.spawn()),
98 &utils::deleteCallbackWrapper<ContainerAdmin*>);
100 if (mRebootCallbackId < 0) {
101 LOGE(mId << ": Failed to register a libvirt reboot callback");
102 virConnectDomainEventDeregisterAny(virDomainGetConnect(mDom.get()),
103 mLifecycleCallbackId);
104 throw ContainerOperationException(mId + ": Failed to register a libvirt reboot callback");
109 ContainerAdmin::~ContainerAdmin()
111 LOGD(mId << ": Destroying ContainerAdmin object...");
113 // Deregister callbacks
114 if (mLifecycleCallbackId >= 0) {
115 virConnectDomainEventDeregisterAny(virDomainGetConnect(mDom.get()),
116 mLifecycleCallbackId);
118 if (mRebootCallbackId >= 0) {
119 virConnectDomainEventDeregisterAny(virDomainGetConnect(mDom.get()),
123 // Try to forcefully stop
124 if (!mDetachOnExit) {
127 } catch (ServerException&) {
128 LOGE(mId << ": Failed to destroy the container");
132 LOGD(mId << ": ContainerAdmin object destroyed");
136 const std::string& ContainerAdmin::getId() const
142 void ContainerAdmin::start()
146 LOGD(mId << ": Starting...");
148 LOGD(mId << ": Already running - nothing to do...");
152 // In order to update daemon without shutting down the containers
153 // autodestroy option must NOT be set. It's best to create domain
154 // without any flags.
155 u_int flags = VIR_DOMAIN_NONE;
157 if (virDomainCreateWithFlags(mDom.get(), flags) < 0) {
158 LOGE(mId << ": Failed to start the container\n"
159 << libvirt::libvirtFormatError());
160 throw ContainerOperationException();
163 LOGD(mId << ": Started");
167 void ContainerAdmin::stop()
171 LOGD(mId << ": Stopping procedure started...");
173 LOGD(mId << ": Already crashed/down/off - nothing to do");
177 utils::Latch stoppedOccured;
179 LifecycleListener setStopped = [&](const int eventId, const int detailId) {
180 if (eventId == VIR_DOMAIN_EVENT_STOPPED) {
181 if (detailId != VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN) {
182 LOGW(mId << ": shutdown requested, but the container stopped with a different status: "
183 << libvirt::libvirtEventDetailToString(eventId, detailId));
185 stoppedOccured.set();
189 ListenerId id = registerLifecycleListener(setStopped, nullptr);
191 bool stopped = stoppedOccured.wait(SHUTDOWN_WAIT);
195 LOGW(mId << ": Gracefull shutdown timed out, the container is still running, destroying");
199 LOGD(mId << ": Stopping procedure ended");
203 void ContainerAdmin::destroy()
207 LOGD(mId << ": Destroying...");
209 LOGD(mId << ": Already crashed/down/off - nothing to do");
213 setSchedulerLevel(SchedulerLevel::FOREGROUND);
215 // Forceful termination of the guest
216 u_int flags = VIR_DOMAIN_DESTROY_DEFAULT;
218 if (virDomainDestroyFlags(mDom.get(), flags) < 0) {
219 LOGE(mId << ": Error while destroying the container:\n"
220 << libvirt::libvirtFormatError());
221 throw ContainerOperationException();
224 LOGD(mId << ": Destroyed");
228 void ContainerAdmin::shutdown()
232 LOGD(mId << ": Shutting down...");
234 LOGD(mId << ": Already crashed/down/off - nothing to do");
238 setSchedulerLevel(SchedulerLevel::FOREGROUND);
240 if (virDomainShutdownFlags(mDom.get(), VIR_DOMAIN_SHUTDOWN_SIGNAL) < 0) {
241 LOGE(mId << ": Error while shutting down the container:\n"
242 << libvirt::libvirtFormatError());
243 throw ContainerOperationException();
246 LOGD(mId << ": Shut down initiated (async)");
250 bool ContainerAdmin::isRunning()
252 return getState() == VIR_DOMAIN_RUNNING;
256 bool ContainerAdmin::isStopped()
258 int state = getState();
259 return state == VIR_DOMAIN_SHUTDOWN ||
260 state == VIR_DOMAIN_SHUTOFF ||
261 state == VIR_DOMAIN_CRASHED;
265 void ContainerAdmin::suspend()
269 LOGD(mId << ": Pausing...");
271 LOGD(mId << ": Already paused - nothing to do...");
275 if (virDomainSuspend(mDom.get()) < 0) {
276 LOGE(mId << ": Error while suspending the container:\n"
277 << libvirt::libvirtFormatError());
278 throw ContainerOperationException();
281 LOGD(mId << ": Paused");
285 void ContainerAdmin::resume()
289 LOGD(mId << ": Resuming...");
291 LOGD(mId << ": Is not paused - nothing to do...");
295 if (virDomainResume(mDom.get()) < 0) {
296 LOGE(mId << ": Error while resuming the container:\n"
297 << libvirt::libvirtFormatError());
298 throw ContainerOperationException();
301 LOGD(mId << ": Resumed");
305 bool ContainerAdmin::isPaused()
307 return getState() == VIR_DOMAIN_PAUSED;
311 int ContainerAdmin::getState()
317 if (virDomainGetState(mDom.get(), &state, NULL, 0)) {
318 LOGE(mId << ": Error while getting the container's state:\n"
319 << libvirt::libvirtFormatError());
320 throw ContainerOperationException();
327 void ContainerAdmin::setSchedulerLevel(SchedulerLevel sched)
330 case SchedulerLevel::FOREGROUND:
331 LOGD(mId << ": Setting SchedulerLevel::FOREGROUND");
332 setSchedulerParams(DEFAULT_CPU_SHARES,
333 DEFAULT_VCPU_PERIOD_MS,
334 mConfig.cpuQuotaForeground);
336 case SchedulerLevel::BACKGROUND:
337 LOGD(mId << ": Setting SchedulerLevel::BACKGROUND");
338 setSchedulerParams(DEFAULT_CPU_SHARES,
339 DEFAULT_VCPU_PERIOD_MS,
340 mConfig.cpuQuotaBackground);
343 assert(0 && "Unknown sched parameter value");
348 void ContainerAdmin::setSchedulerParams(std::uint64_t cpuShares, std::uint64_t vcpuPeriod, std::int64_t vcpuQuota)
353 int numParamsBuff = 0;
355 std::unique_ptr<virTypedParameter[]> params(new virTypedParameter[maxParams]);
357 virTypedParameterPtr paramsTmp = params.get();
359 virTypedParamsAddULLong(¶msTmp, &numParamsBuff, &maxParams, VIR_DOMAIN_SCHEDULER_CPU_SHARES, cpuShares);
360 virTypedParamsAddULLong(¶msTmp, &numParamsBuff, &maxParams, VIR_DOMAIN_SCHEDULER_VCPU_PERIOD, vcpuPeriod);
361 virTypedParamsAddLLong(¶msTmp, &numParamsBuff, &maxParams, VIR_DOMAIN_SCHEDULER_VCPU_QUOTA, vcpuQuota);
363 if (virDomainSetSchedulerParameters(mDom.get(), params.get(), numParamsBuff) < 0) {
364 LOGE(mId << ": Error while setting the container's scheduler params:\n"
365 << libvirt::libvirtFormatError());
366 throw ContainerOperationException();
370 void ContainerAdmin::setDetachOnExit()
372 mDetachOnExit = true;
375 std::int64_t ContainerAdmin::getSchedulerQuota()
380 std::unique_ptr<char, void(*)(void*)> type(virDomainGetSchedulerType(mDom.get(), &numParamsBuff), free);
382 if (type == NULL || numParamsBuff <= 0 || strcmp(type.get(), "posix") != 0) {
383 LOGE(mId << ": Error while getting the container's scheduler type:\n"
384 << libvirt::libvirtFormatError());
385 throw ContainerOperationException();
388 std::unique_ptr<virTypedParameter[]> params(new virTypedParameter[numParamsBuff]);
390 if (virDomainGetSchedulerParameters(mDom.get(), params.get(), &numParamsBuff) < 0) {
391 LOGE(mId << ": Error while getting the container's scheduler params:\n"
392 << libvirt::libvirtFormatError());
393 throw ContainerOperationException();
397 if (virTypedParamsGetLLong(params.get(),
399 VIR_DOMAIN_SCHEDULER_VCPU_QUOTA,
401 LOGE(mId << ": Error while getting the container's scheduler quota param:\n"
402 << libvirt::libvirtFormatError());
403 throw ContainerOperationException();
409 ContainerAdmin::ListenerId ContainerAdmin::registerLifecycleListener(const ContainerAdmin::LifecycleListener& listener,
410 const utils::CallbackGuard::Tracker& tracker)
413 utils::CallbackWrapper<LifecycleListener> wrap(listener, tracker);
415 std::unique_lock<std::mutex> lock(mListenerMutex);
416 unsigned int id = mNextIdForListener++;
417 mLifecycleListeners.insert(LifecycleListenerMap::value_type(id, std::move(wrap)));
422 ContainerAdmin::ListenerId ContainerAdmin::registerRebootListener(const ContainerAdmin::RebootListener& listener,
423 const utils::CallbackGuard::Tracker& tracker)
426 utils::CallbackWrapper<RebootListener> wrap(listener, tracker);
428 std::unique_lock<std::mutex> lock(mListenerMutex);
429 unsigned int id = mNextIdForListener++;
430 mRebootListeners.insert(RebootListenerMap::value_type(id, std::move(wrap)));
435 void ContainerAdmin::removeListener(const ContainerAdmin::ListenerId id)
437 std::unique_lock<std::mutex> lock(mListenerMutex);
438 mLifecycleListeners.erase(id);
439 mRebootListeners.erase(id);
442 int ContainerAdmin::libvirtLifecycleCallback(virConnectPtr /*con*/,
443 virDomainPtr /*dom*/,
448 ContainerAdmin* thisPtr = utils::getCallbackFromPointer<ContainerAdmin*>(opaque);
450 LOGI(thisPtr->getId()
451 << ": Lifecycle event: "
452 << libvirt::libvirtEventToString(event)
454 << libvirt::libvirtEventDetailToString(event, detail));
456 std::unique_lock<std::mutex> lock(thisPtr->mListenerMutex);
457 for (auto& it : thisPtr->mLifecycleListeners) {
458 LifecycleListener f = it.second.get();
462 // ignored, libvirt's legacy
466 void ContainerAdmin::libvirtRebootCallback(virConnectPtr /*con*/,
467 virDomainPtr /*dom*/,
470 ContainerAdmin* thisPtr = utils::getCallbackFromPointer<ContainerAdmin*>(opaque);
472 LOGI(thisPtr->getId() << ": Reboot event");
474 std::unique_lock<std::mutex> lock(thisPtr->mListenerMutex);
475 for (auto& it : thisPtr->mRebootListeners) {
476 RebootListener f = it.second.get();
482 } // namespace security_containers