From 34e721714c319bad60e60cbbf6e22f37ccb996b7 Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Fri, 14 Nov 2014 12:20:04 +0100 Subject: [PATCH] Fix shutdown for systemd init [Bug/Feature] Systemd does not shutdown on signal [Cause] N/A [Solution] N/A [Verification] Build, install, run tests, run container Change-Id: Ic4b617c1a35a260803961fb17aba1da51c3af013 --- common/lxc/domain.cpp | 43 ++++++++++++ common/lxc/domain.hpp | 2 + common/utils/initctl.cpp | 90 +++++++++++++++++++++++++ common/utils/initctl.hpp | 44 ++++++++++++ server/configs/lxc-templates/business.sh | 4 +- server/configs/lxc-templates/private.sh | 4 +- tests/unit_tests/lxc/templates/minimal-dbus1.sh | 7 +- tests/unit_tests/lxc/templates/minimal-dbus2.sh | 7 +- tests/unit_tests/lxc/templates/minimal-dbus3.sh | 7 +- tests/unit_tests/lxc/templates/minimal.sh | 7 +- 10 files changed, 209 insertions(+), 6 deletions(-) create mode 100644 common/utils/initctl.cpp create mode 100644 common/utils/initctl.hpp diff --git a/common/lxc/domain.cpp b/common/lxc/domain.cpp index 0959959..57228c0 100644 --- a/common/lxc/domain.cpp +++ b/common/lxc/domain.cpp @@ -30,6 +30,9 @@ #include #include +#include +#include + #include namespace security_containers { @@ -145,6 +148,26 @@ bool LxcDomain::reboot() bool LxcDomain::shutdown(int timeout) { + State state = getState(); + if (state == State::STOPPED) { + return true; + } + if (state != State::RUNNING) { + LOGE("Could not gracefully shutdown domain " << getName()); + return false; + } + + // try shutdown by sending poweroff to init + if (setRunLevel(utils::RUNLEVEL_POWEROFF)) { + if (!mContainer->wait(mContainer, "STOPPED", timeout)) { + LOGE("Could not gracefully shutdown domain " + getName() + " in " << timeout << "s"); + return false; + } + return true; + } + LOGW("SetRunLevel failed for domain " + getName()); + + // fallback for other inits like bash: lxc sends 'lxc.haltsignal' signal to init if (!mContainer->shutdown(mContainer, timeout)) { LOGE("Could not gracefully shutdown domain " + getName() + " in " << timeout << "s"); return false; @@ -170,6 +193,26 @@ bool LxcDomain::unfreeze() return true; } +bool LxcDomain::setRunLevel(int runLevel) +{ + auto callback = [](void* param) { + utils::RunLevel runLevel = *reinterpret_cast(param); + return utils::setRunLevel(runLevel) ? 0 : 1; + }; + + lxc_attach_options_t options = LXC_ATTACH_OPTIONS_DEFAULT; + pid_t pid; + int ret = mContainer->attach(mContainer, callback, &runLevel, &options, &pid); + if (ret != 0) { + return false; + } + int status; + if (waitpid(pid, &status, 0) < 0) { + return false; + } + return status == 0; +} + } // namespace lxc } // namespace security_containers diff --git a/common/lxc/domain.hpp b/common/lxc/domain.hpp index 3202f54..8421d6d 100644 --- a/common/lxc/domain.hpp +++ b/common/lxc/domain.hpp @@ -76,6 +76,8 @@ public: bool unfreeze(); private: lxc_container* mContainer; + + bool setRunLevel(int runLevel); }; diff --git a/common/utils/initctl.cpp b/common/utils/initctl.cpp new file mode 100644 index 0000000..cfdea77 --- /dev/null +++ b/common/utils/initctl.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Piotr Bartosiewicz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * @brief Api for talking to init via initctl + */ + +#include "config.hpp" +#include "utils/initctl.hpp" + +#include +#include +#include +#include + +namespace security_containers { +namespace utils { + +namespace { + struct InitctlRequest { + int magic; + int cmd; + int runlevel; + int sleeptime; + char data[368]; + }; + const int INITCTL_MAGIC = 0x03091969; + const int INITCTL_CMD_RUNLVL = 1; + + bool write(int fd, const void* data, size_t size) + { + while (size > 0) { + ssize_t r = ::write(fd, data, size); + if (r < 0) { + if (errno == EINTR) { + continue; + } + return false; + } + size -= r; + data = reinterpret_cast(data) + r; + } + return true; + } + + void close(int fd) + { + while (::close(fd) == -1 && errno == EINTR) {} + } +} + +bool setRunLevel(RunLevel runLevel) +{ + int fd = ::open("/dev/initctl", O_WRONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY); + if (fd < 0) { + return false; + } + + InitctlRequest req; + memset(&req, 0, sizeof(req)); + req.magic = INITCTL_MAGIC; + req.cmd = INITCTL_CMD_RUNLVL; + req.runlevel = '0' + runLevel; + req.sleeptime = 0; + + bool ret = write(fd, &req, sizeof(req)); + close(fd); + return ret; +} + + +} // namespace utils +} // namespace security_containers diff --git a/common/utils/initctl.hpp b/common/utils/initctl.hpp new file mode 100644 index 0000000..2b97dd2 --- /dev/null +++ b/common/utils/initctl.hpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Piotr Bartosiewicz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com) + * @brief Api for talking to init via initctl + */ + +#ifndef COMMON_UTILS_INITCTL_HPP +#define COMMON_UTILS_INITCTL_HPP + + +namespace security_containers { +namespace utils { + +enum RunLevel : int { + RUNLEVEL_POWEROFF = 0, + RUNLEVEL_REBOOT = 6 +}; + +bool setRunLevel(RunLevel runLevel); + + +} // namespace utils +} // namespace security_containers + + +#endif // COMMON_UTILS_INITCTL_HPP diff --git a/server/configs/lxc-templates/business.sh b/server/configs/lxc-templates/business.sh index 75be9e6..09d67ca 100755 --- a/server/configs/lxc-templates/business.sh +++ b/server/configs/lxc-templates/business.sh @@ -27,7 +27,9 @@ cat <> ${path}/config lxc.utsname = ${name} lxc.rootfs = ${rootfs} -lxc.haltsignal = SIGTERM +# userns 1-to-1 mapping +#lxc.id_map = u 0 0 65536 +#lxc.id_map = g 0 0 65536 lxc.pts = 256 lxc.tty = 0 diff --git a/server/configs/lxc-templates/private.sh b/server/configs/lxc-templates/private.sh index 2926e55..731ff72 100755 --- a/server/configs/lxc-templates/private.sh +++ b/server/configs/lxc-templates/private.sh @@ -27,7 +27,9 @@ cat <> ${path}/config lxc.utsname = ${name} lxc.rootfs = ${rootfs} -lxc.haltsignal = SIGTERM +# userns 1-to-1 mapping +#lxc.id_map = u 0 0 65536 +#lxc.id_map = g 0 0 65536 lxc.pts = 256 lxc.tty = 0 diff --git a/tests/unit_tests/lxc/templates/minimal-dbus1.sh b/tests/unit_tests/lxc/templates/minimal-dbus1.sh index 6f967e5..35f816f 100755 --- a/tests/unit_tests/lxc/templates/minimal-dbus1.sh +++ b/tests/unit_tests/lxc/templates/minimal-dbus1.sh @@ -45,18 +45,23 @@ cat <> ${path}/config lxc.utsname = ${name} lxc.rootfs = ${rootfs} +# userns 1-to-1 mapping +lxc.id_map = u 0 0 65536 +lxc.id_map = g 0 0 65536 + lxc.haltsignal = SIGTERM lxc.pts = 256 lxc.tty = 0 +lxc.cgroup.devices.deny = a + lxc.mount.auto = proc sys cgroup lxc.mount.entry = /bin bin none ro,bind 0 0 lxc.mount.entry = /etc etc none ro,bind 0 0 lxc.mount.entry = /lib lib none ro,bind 0 0 lxc.mount.entry = /sbin sbin none ro,bind 0 0 lxc.mount.entry = /usr usr none ro,rbind 0 0 -lxc.mount.entry = devtmpfs dev devtmpfs rw,relatime,mode=755 0 0 lxc.mount.entry = /tmp/ut-run1 var/run none rw,bind 0 0 EOF diff --git a/tests/unit_tests/lxc/templates/minimal-dbus2.sh b/tests/unit_tests/lxc/templates/minimal-dbus2.sh index 1b5bf57..f8f963e 100755 --- a/tests/unit_tests/lxc/templates/minimal-dbus2.sh +++ b/tests/unit_tests/lxc/templates/minimal-dbus2.sh @@ -45,18 +45,23 @@ cat <> ${path}/config lxc.utsname = ${name} lxc.rootfs = ${rootfs} +# userns 1-to-1 mapping +lxc.id_map = u 0 0 65536 +lxc.id_map = g 0 0 65536 + lxc.haltsignal = SIGTERM lxc.pts = 256 lxc.tty = 0 +lxc.cgroup.devices.deny = a + lxc.mount.auto = proc sys cgroup lxc.mount.entry = /bin bin none ro,bind 0 0 lxc.mount.entry = /etc etc none ro,bind 0 0 lxc.mount.entry = /lib lib none ro,bind 0 0 lxc.mount.entry = /sbin sbin none ro,bind 0 0 lxc.mount.entry = /usr usr none ro,rbind 0 0 -lxc.mount.entry = devtmpfs dev devtmpfs rw,relatime,mode=755 0 0 lxc.mount.entry = /tmp/ut-run2 var/run none rw,bind 0 0 EOF diff --git a/tests/unit_tests/lxc/templates/minimal-dbus3.sh b/tests/unit_tests/lxc/templates/minimal-dbus3.sh index 9ace1c6..68f4f11 100755 --- a/tests/unit_tests/lxc/templates/minimal-dbus3.sh +++ b/tests/unit_tests/lxc/templates/minimal-dbus3.sh @@ -45,18 +45,23 @@ cat <> ${path}/config lxc.utsname = ${name} lxc.rootfs = ${rootfs} +# userns 1-to-1 mapping +lxc.id_map = u 0 0 65536 +lxc.id_map = g 0 0 65536 + lxc.haltsignal = SIGTERM lxc.pts = 256 lxc.tty = 0 +lxc.cgroup.devices.deny = a + lxc.mount.auto = proc sys cgroup lxc.mount.entry = /bin bin none ro,bind 0 0 lxc.mount.entry = /etc etc none ro,bind 0 0 lxc.mount.entry = /lib lib none ro,bind 0 0 lxc.mount.entry = /sbin sbin none ro,bind 0 0 lxc.mount.entry = /usr usr none ro,rbind 0 0 -lxc.mount.entry = devtmpfs dev devtmpfs rw,relatime,mode=755 0 0 lxc.mount.entry = /tmp/ut-run3 var/run none rw,bind 0 0 EOF diff --git a/tests/unit_tests/lxc/templates/minimal.sh b/tests/unit_tests/lxc/templates/minimal.sh index 64f6da7..547661e 100755 --- a/tests/unit_tests/lxc/templates/minimal.sh +++ b/tests/unit_tests/lxc/templates/minimal.sh @@ -43,18 +43,23 @@ cat <> ${path}/config lxc.utsname = ${name} lxc.rootfs = ${rootfs} +# userns 1-to-1 mapping +lxc.id_map = u 0 0 65536 +lxc.id_map = g 0 0 65536 + lxc.haltsignal = SIGTERM lxc.pts = 256 lxc.tty = 0 +lxc.cgroup.devices.deny = a + lxc.mount.auto = proc sys cgroup lxc.mount.entry = /bin bin none ro,bind 0 0 lxc.mount.entry = /etc etc none ro,bind 0 0 lxc.mount.entry = /lib lib none ro,bind 0 0 lxc.mount.entry = /sbin sbin none ro,bind 0 0 lxc.mount.entry = /usr usr none ro,rbind 0 0 -lxc.mount.entry = devtmpfs dev devtmpfs rw,relatime,mode=755 0 0 EOF if [ "$(uname -m)" = "x86_64" ]; then -- 2.7.4