From 11ddb3bab10b043797aca9eaa491070aa8510f3d Mon Sep 17 00:00:00 2001 From: Piotr Bartosiewicz Date: Thu, 18 Sep 2014 10:07:15 +0200 Subject: [PATCH] New get_container_id_by_pid API function [Bug/Feature] Introduce new API function: sc_get_container_id_by_pid [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: I8bee78c062bcbbe29fc9e2c651989570c26869d1 --- client/security-containers-client-impl.cpp | 36 +++++++++ client/security-containers-client-impl.hpp | 5 ++ client/security-containers-client.cpp | 5 ++ client/security-containers-client.h | 11 +++ client/utils.cpp | 118 ++++++++++++++++++++++++++++ client/utils.hpp | 32 ++++++++ tests/unit_tests/client/ut-client-utils.cpp | 55 +++++++++++++ tests/unit_tests/client/ut-client.cpp | 37 +++++++++ 8 files changed, 299 insertions(+) create mode 100644 client/utils.cpp create mode 100644 client/utils.hpp create mode 100644 tests/unit_tests/client/ut-client-utils.cpp diff --git a/client/security-containers-client-impl.cpp b/client/security-containers-client-impl.cpp index 8486ae7..a2c51ba 100644 --- a/client/security-containers-client-impl.cpp +++ b/client/security-containers-client-impl.cpp @@ -25,6 +25,7 @@ #include #include "security-containers-client-impl.hpp" +#include "utils.hpp" #include #include #include @@ -33,6 +34,7 @@ #include #include +#include using namespace std; using namespace dbus; @@ -122,6 +124,17 @@ ScStatus toStatus(const std::exception& ex) return SCCLIENT_OTHER_ERROR; } +bool readFirstLineOfFile(const std::string& path, std::string& ret) +{ + std::ifstream file(path); + if (!file) { + return false; + } + + std::getline(file, ret); + return true; +} + } //namespace ScStatus Client::sc_start_glib_loop() noexcept @@ -309,6 +322,29 @@ ScStatus Client::sc_get_active_container_id(ScString* id) noexcept return ret; } +ScStatus Client::sc_get_container_id_by_pid(int pid, ScString* id) noexcept +{ + assert(id); + + const std::string path = "/proc/" + std::to_string(pid) + "/cpuset"; + + std::string cpuset; + if (!readFirstLineOfFile(path, cpuset)) { + mStatus = Status(SCCLIENT_INVALID_ARGUMENT, "Process not found"); + return sc_get_status(); + } + + std::string containerId; + if (!parseContainerIdFromCpuSet(cpuset, containerId)) { + mStatus = Status(SCCLIENT_OTHER_ERROR, "unknown format of cpuset"); + return sc_get_status(); + } + + *id = strdup(containerId.c_str()); + mStatus = Status(); + return sc_get_status();; +} + ScStatus Client::sc_set_active_container(const char* id) noexcept { assert(id); diff --git a/client/security-containers-client-impl.hpp b/client/security-containers-client-impl.hpp index 10aecaf..f1bfacb 100644 --- a/client/security-containers-client-impl.hpp +++ b/client/security-containers-client-impl.hpp @@ -117,6 +117,11 @@ public: ScStatus sc_get_active_container_id(ScString* id) noexcept; /** + * @see ::sc_get_container_id_by_pid + */ + ScStatus sc_get_container_id_by_pid(int pid, ScString* id) noexcept; + + /** * @see ::sc_set_active_container */ ScStatus sc_set_active_container(const char* id) noexcept; diff --git a/client/security-containers-client.cpp b/client/security-containers-client.cpp index b1eb521..cb22544 100644 --- a/client/security-containers-client.cpp +++ b/client/security-containers-client.cpp @@ -121,6 +121,11 @@ API ScStatus sc_get_active_container_id(ScClient client, ScString* id) return getClient(client).sc_get_active_container_id(id); } +API ScStatus sc_get_container_id_by_pid(ScClient client, int pid, ScString* id) +{ + return getClient(client).sc_get_container_id_by_pid(pid, id); +} + API ScStatus sc_set_active_container(ScClient client, const char* id) { return getClient(client).sc_set_active_container(id); diff --git a/client/security-containers-client.h b/client/security-containers-client.h index c56e9de..4d37c3c 100644 --- a/client/security-containers-client.h +++ b/client/security-containers-client.h @@ -247,6 +247,17 @@ ScStatus sc_get_container_ids(ScClient client, ScArrayString* array); ScStatus sc_get_active_container_id(ScClient client, ScString* id); /** + * Get container name of process with given pid. + * + * @param[in] client security-containers-server's client + * @param[in] pid process id + * @param[out] id active container name + * @return status of this function call + * @remark Use @p sc_string_free() to free memory occupied by @p id. + */ +ScStatus sc_get_container_id_by_pid(ScClient client, int pid, ScString* id); + +/** * Set active (foreground) container. * * @param[in] client security-containers-server's client diff --git a/client/utils.cpp b/client/utils.cpp new file mode 100644 index 0000000..98b6905 --- /dev/null +++ b/client/utils.cpp @@ -0,0 +1,118 @@ +/* + * 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 Utility functions definition + */ + +#include "config.hpp" +#include "utils.hpp" + +#include + +namespace { + +const std::string CPUSET_HOST = "/"; +const std::string CPUSET_LIBVIRT_PREFIX_OLD = "/machine/"; +const std::string CPUSET_LIBVIRT_SUFFIX_OLD = ".libvirt-lxc"; +const std::string CPUSET_LIBVIRT_PREFIX = "/machine.slice/machine-lxc\\x2d"; +const std::string CPUSET_LIBVIRT_SUFFIX = ".scope"; + +bool parseOldFormat(const std::string& cpuset, std::string& id) +{ + // '/machine/.libvirt-lxc' + if (!boost::starts_with(cpuset, CPUSET_LIBVIRT_PREFIX_OLD)) { + return false; + } + + if (!boost::ends_with(cpuset, CPUSET_LIBVIRT_SUFFIX_OLD)) { + return false; + } + + id.assign(cpuset, + CPUSET_LIBVIRT_PREFIX_OLD.size(), + cpuset.size() - CPUSET_LIBVIRT_PREFIX_OLD.size() - CPUSET_LIBVIRT_SUFFIX_OLD.size()); + return true; +} + +inline int unhex(char c) +{ + if (c >= '0' && c <= '9') { + return c - '0'; + } + if (c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } + if (c >= 'A' && c <= 'F') { + return c - 'A' + 10; + } + return -1; +} + +void unescape(std::string& value) +{ + const size_t len = value.size(); + size_t inPos = 0; + size_t outPos = 0; + while (inPos < len) { + const char c = value[inPos++]; + if (c == '-') { + value[outPos++] = '/'; + } else if (c == '\\' && value[inPos] == 'x') { + const char a = unhex(value[inPos+1]); + const char b = unhex(value[inPos+2]); + value[outPos++] = (char) ((a << 4) | b); + inPos += 3; + } else { + value[outPos++] = c; + } + } + value.resize(outPos); +} + +bool parseNewFormat(const std::string& cpuset, std::string& id) +{ + // '/machine.slice/machine-lxc\x2d.scope' + if (!boost::starts_with(cpuset, CPUSET_LIBVIRT_PREFIX)) { + return false; + } + + if (!boost::ends_with(cpuset, CPUSET_LIBVIRT_SUFFIX)) { + return false; + } + + id = cpuset.substr(CPUSET_LIBVIRT_PREFIX.size(), + cpuset.size() - CPUSET_LIBVIRT_PREFIX.size() - CPUSET_LIBVIRT_SUFFIX.size()); + unescape(id); + return true; +} + +} // namespace + +bool parseContainerIdFromCpuSet(const std::string& cpuset, std::string& id) +{ + if (cpuset == CPUSET_HOST) { + id = "host"; + return true; + } + + return parseNewFormat(cpuset, id) || parseOldFormat(cpuset, id); +} + diff --git a/client/utils.hpp b/client/utils.hpp new file mode 100644 index 0000000..49b83c0 --- /dev/null +++ b/client/utils.hpp @@ -0,0 +1,32 @@ +/* + * 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 Utility functions declaration + */ + +#ifndef SECURITY_CONTAINERS_CLIENT_UTILS_HPP +#define SECURITY_CONTAINERS_CLIENT_UTILS_HPP + +#include + +bool parseContainerIdFromCpuSet(const std::string& cpuset, std::string& id); + +#endif // SECURITY_CONTAINERS_CLIENT_UTILS_HPP diff --git a/tests/unit_tests/client/ut-client-utils.cpp b/tests/unit_tests/client/ut-client-utils.cpp new file mode 100644 index 0000000..ac4c1fd --- /dev/null +++ b/tests/unit_tests/client/ut-client-utils.cpp @@ -0,0 +1,55 @@ +/* + * 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 Unit tests of the client utils + */ + +#include +#include "ut.hpp" +#include + + +BOOST_AUTO_TEST_SUITE(ClientUtils) + +BOOST_AUTO_TEST_CASE(ParseContainerIdFromCpuSetTest) +{ + auto testBad = [](const std::string& input) { + std::string ret; + BOOST_CHECK(!parseContainerIdFromCpuSet(input, ret)); + }; + + auto testOK = [](const std::string& input, const std::string& expected) { + std::string ret; + BOOST_CHECK(parseContainerIdFromCpuSet(input, ret)); + BOOST_CHECK_EQUAL(expected, ret); + }; + + testBad(""); + testBad("/foo"); + + testOK("/", "host"); + testOK("/machine/a-b.libvirt-lxc", "a-b"); + testOK("/machine.slice/machine-lxc\\x2da\\x2db.scope", "a-b"); + testOK("/machine.slice/machine-lxc\\x2da-b.scope", "a/b"); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/unit_tests/client/ut-client.cpp b/tests/unit_tests/client/ut-client.cpp index bbf2013..574be39 100644 --- a/tests/unit_tests/client/ut-client.cpp +++ b/tests/unit_tests/client/ut-client.cpp @@ -256,4 +256,41 @@ BOOST_AUTO_TEST_CASE(NotificationTest) } } +BOOST_AUTO_TEST_CASE(GetContainerIdByPidTest1) +{ + ScClient client = sc_client_create(); + ScString container; + ScStatus status = sc_get_container_id_by_pid(client, 1, &container); + BOOST_REQUIRE_EQUAL(SCCLIENT_SUCCESS, status); + + BOOST_CHECK_EQUAL(container, std::string("host")); + + sc_string_free(container); + sc_client_free(client); +} + +BOOST_AUTO_TEST_CASE(GetContainerIdByPidTest2) +{ + std::set ids; + + ScClient client = sc_client_create(); + for (int n = 0; n < 100000; ++n) { + ScString container; + ScStatus status = sc_get_container_id_by_pid(client, n, &container); + if (status == SCCLIENT_SUCCESS) { + ids.insert(container); + sc_string_free(container); + } else { + BOOST_WARN_MESSAGE(status == SCCLIENT_INVALID_ARGUMENT, sc_get_status_message(client)); + } + } + sc_client_free(client); + + BOOST_CHECK(ids.count("host") == 1); + + for (const auto& dbus : EXPECTED_DBUSES_STARTED) { + BOOST_CHECK(ids.count(dbus.first) == 1); + } +} + BOOST_AUTO_TEST_SUITE_END() -- 2.7.4