New get_container_id_by_pid API function 23/27723/7
authorPiotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
Thu, 18 Sep 2014 08:07:15 +0000 (10:07 +0200)
committerPiotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
Wed, 24 Sep 2014 14:50:23 +0000 (16:50 +0200)
[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
client/security-containers-client-impl.hpp
client/security-containers-client.cpp
client/security-containers-client.h
client/utils.cpp [new file with mode: 0644]
client/utils.hpp [new file with mode: 0644]
tests/unit_tests/client/ut-client-utils.cpp [new file with mode: 0644]
tests/unit_tests/client/ut-client.cpp

index 8486ae7..a2c51ba 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <config.hpp>
 #include "security-containers-client-impl.hpp"
+#include "utils.hpp"
 #include <dbus/connection.hpp>
 #include <dbus/exception.hpp>
 #include <utils/glib-loop.hpp>
@@ -33,6 +34,7 @@
 
 #include <cstring>
 #include <cassert>
+#include <fstream>
 
 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);
index 10aecaf..f1bfacb 100644 (file)
@@ -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;
index b1eb521..cb22544 100644 (file)
@@ -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);
index c56e9de..4d37c3c 100644 (file)
@@ -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 (file)
index 0000000..98b6905
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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 <boost/algorithm/string.hpp>
+
+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/<id>.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<id>.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 (file)
index 0000000..49b83c0
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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 <string>
+
+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 (file)
index 0000000..ac4c1fd
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
+ *
+ *  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 <config.hpp>
+#include "ut.hpp"
+#include <utils.hpp>
+
+
+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()
index bbf2013..574be39 100644 (file)
@@ -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<std::string> 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()