From 59f52b01c6034c5895893266873b32d9761d7381 Mon Sep 17 00:00:00 2001 From: Krzysztof Dynowski Date: Mon, 5 Oct 2015 11:01:14 +0200 Subject: [PATCH 01/16] fix clang build [Featurei/Bug] clang fails parsing doxygen comment [Cause] N/A [Solution] change param reference [Verification] Build with clnag Change-Id: I65d461202d1d6af9bdbfcc89bc104b006e78bf23 --- libs/lxcpp/network.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/lxcpp/network.hpp b/libs/lxcpp/network.hpp index a1ebf49..63a6cd2 100644 --- a/libs/lxcpp/network.hpp +++ b/libs/lxcpp/network.hpp @@ -230,7 +230,7 @@ public: * Create bridge interface * ip link add @mIfname type bridge * Create psedo-ethernet interface on existing one - * ip link add @mIfname type macvlan link @peerif [mode @mode] + * ip link add @mIfname type macvlan link @peerif [mode @a mode] */ void create(InterfaceType type, const std::string& peerif, MacVLanMode mode = MacVLanMode::PRIVATE); -- 2.7.4 From 2d358443b1b18f8fb2ca57342552e5a8c049bec5 Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Tue, 6 Oct 2015 14:42:28 +0200 Subject: [PATCH 02/16] Fix buildbreak [Featurei/Bug] Missing include [Cause] N/A [Solution] N/A [Verification] Clean and build with clang Change-Id: I76da0251f02449561a60dfb20ccf2504b46789b3 --- libs/lxcpp/network.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/lxcpp/network.hpp b/libs/lxcpp/network.hpp index 63a6cd2..0affc5f 100644 --- a/libs/lxcpp/network.hpp +++ b/libs/lxcpp/network.hpp @@ -24,7 +24,8 @@ #ifndef LXCPP_NETWORK_HPP #define LXCPP_NETWORK_HPP -#include "config/fields.hpp" +#include +#include #include #include -- 2.7.4 From 77c97e65c4c291d98913da3325497c86f4957733 Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Wed, 30 Sep 2015 12:40:16 +0200 Subject: [PATCH 03/16] config: std::pair serialization [Feature] Serialization of pairs [Cause] N/A [Solution] N/A [Verification] Build, install, run tests. Change-Id: I5c860e72d91491699deace1c50e213b0538440a4 --- libs/config/from-fdstore-visitor.hpp | 8 ++++ libs/config/from-gvariant-visitor.hpp | 25 ++++++++++ libs/config/from-json-visitor.hpp | 22 +++++++++ libs/config/from-kvjson-visitor.hpp | 32 +++++++++++-- libs/config/kvstore.hpp | 48 +++++++++++++++++++ libs/config/to-fdstore-visitor.hpp | 9 ++++ libs/config/to-gvariant-visitor.hpp | 46 ++++++++++++++---- libs/config/to-json-visitor.hpp | 25 +++++++++- libs/config/visit-fields.hpp | 65 ++++++++++++++++++++++++++ tests/unit_tests/config/testconfig-example.hpp | 16 +++++-- tests/unit_tests/config/ut-configuration.cpp | 3 ++ 11 files changed, 281 insertions(+), 18 deletions(-) create mode 100644 libs/config/visit-fields.hpp diff --git a/libs/config/from-fdstore-visitor.hpp b/libs/config/from-fdstore-visitor.hpp index 1013a61..f0c8dbe 100644 --- a/libs/config/from-fdstore-visitor.hpp +++ b/libs/config/from-fdstore-visitor.hpp @@ -28,9 +28,11 @@ #include "config/is-visitable.hpp" #include "config/fdstore.hpp" #include "config/types.hpp" +#include "config/visit-fields.hpp" #include #include +#include #include namespace config { @@ -110,6 +112,12 @@ private: readInternal(value); } } + + template + void readInternal(std::pair& values) + { + visitFields(values, this, std::string()); + } }; } // namespace config diff --git a/libs/config/from-gvariant-visitor.hpp b/libs/config/from-gvariant-visitor.hpp index 213f917..afc1beb 100644 --- a/libs/config/from-gvariant-visitor.hpp +++ b/libs/config/from-gvariant-visitor.hpp @@ -29,12 +29,15 @@ #include "config/exception.hpp" #include "config/is-union.hpp" #include "config/types.hpp" +#include "config/visit-fields.hpp" #include #include #include +#include #include #include + #include namespace config { @@ -175,6 +178,28 @@ private: } } + struct HelperVisitor + { + template + static void visit(GVariantIter* iter, T&& value) + { + auto child = makeUnique(g_variant_iter_next_value(iter)); + fromGVariant(child.get(), value); + } + }; + + template + static void fromGVariant(GVariant* object, std::pair& values) + { + checkType(object, G_VARIANT_TYPE_ARRAY); + + GVariantIter iter; + g_variant_iter_init(&iter, object); + + HelperVisitor visitor; + visitFields(values, &visitor, &iter); + } + template static typename std::enable_if::value>::type fromGVariant(GVariant* object, T& value) diff --git a/libs/config/from-json-visitor.hpp b/libs/config/from-json-visitor.hpp index 041027e..9bdf19b 100644 --- a/libs/config/from-json-visitor.hpp +++ b/libs/config/from-json-visitor.hpp @@ -27,12 +27,14 @@ #include "config/is-visitable.hpp" #include "config/exception.hpp" +#include "config/visit-fields.hpp" #include #include #include #include #include +#include namespace config { @@ -170,6 +172,26 @@ private: } } + struct HelperVisitor + { + template + static void visit(json_object* object, std::size_t& idx, T&& value) + { + fromJsonObject(json_object_array_get_idx(object, idx), value); + idx += 1; + } + }; + + template + static void fromJsonObject(json_object* object, std::pair& values) + { + checkType(object, json_type_array); + + std::size_t idx = 0; + HelperVisitor visitor; + visitFields(values, &visitor, object, idx); + } + template::value>::type> static void fromJsonObject(json_object* object, T& value) { diff --git a/libs/config/from-kvjson-visitor.hpp b/libs/config/from-kvjson-visitor.hpp index 96f3888..d9dbce7 100644 --- a/libs/config/from-kvjson-visitor.hpp +++ b/libs/config/from-kvjson-visitor.hpp @@ -28,7 +28,9 @@ #include "config/from-kvstore-visitor.hpp" #include "config/is-union.hpp" +#include "config/visit-fields.hpp" +#include #include namespace config { @@ -65,6 +67,12 @@ public: getValue(name, value); } + template + void visit(std::size_t& idx, T& value) { + getValue(idx, value); + idx += 1; + } + private: KVStore& mStore; std::string mKeyPrefix; @@ -105,14 +113,13 @@ private: std::string k = key(mKeyPrefix, name); if (mStore.exists(k)) { t = mStore.get(k); - } - else { + } else { json_object* object = nullptr; if (mObject) { json_object_object_get_ex(mObject, name.c_str(), &object); } if (!object) { - throw ConfigException("Missing json field " + key(mKeyPrefix, name)); + throw ConfigException("Missing json field " + k); } fromJsonObject(object, t); } @@ -186,6 +193,25 @@ private: } } + template + void getValue(const std::string& name, std::pair& values) + { + json_object* object = nullptr; + if (mObject && json_object_object_get_ex(mObject, name.c_str(), &object)) { + checkType(object, json_type_array); + } + + std::string k = key(mKeyPrefix, name); + FromKVJsonVisitor visitor(*this, name, false); + if (mStore.exists(k)) { + json_object_put(visitor.mObject); + visitor.mObject = nullptr; + } + + std::size_t idx = 0; + visitFields(values, &visitor, idx); + } + template::value, int>::type = 0> void getValue(int i, T& t) { diff --git a/libs/config/kvstore.hpp b/libs/config/kvstore.hpp index fe30e21..1206d08 100644 --- a/libs/config/kvstore.hpp +++ b/libs/config/kvstore.hpp @@ -27,6 +27,7 @@ #include "config/sqlite3/connection.hpp" #include "config/sqlite3/statement.hpp" +#include "config/visit-fields.hpp" #include #include @@ -36,6 +37,7 @@ #include #include #include +#include namespace config { @@ -143,6 +145,8 @@ private: void setInternal(const std::string& key, const std::vector& values); template void setInternal(const std::string& key, const std::array& values); + template + void setInternal(const std::string& key, const std::pair& values); std::string getInternal(const std::string& key, std::string*); char* getInternal(const std::string& key, char**); @@ -153,6 +157,8 @@ private: std::vector getInternal(const std::string& key, std::vector*); template std::array getInternal(const std::string& key, std::array*); + template + std::pair getInternal(const std::string& key, std::pair*); std::string mPath; sqlite3::Connection mConn; @@ -221,6 +227,26 @@ void KVStore::setInternal(const std::string& key, const std::array& values setInternal(key, strValues); } +struct SetTupleVisitor +{ + template + static void visit(std::vector::iterator& it, const T& value) + { + *it = toString(value); + ++it; + } +}; + +template +void KVStore::setInternal(const std::string& key, const std::pair& values) +{ + std::vector strValues(std::tuple_size>::value); + + SetTupleVisitor visitor; + visitFields(values, &visitor, strValues.begin()); + setInternal(key, strValues); +} + template T KVStore::getInternal(const std::string& key, T*) { @@ -255,6 +281,28 @@ std::vector KVStore::getInternal(const std::string& key, std::vector*) return values; } +struct GetTupleVisitor +{ + template + static void visit(std::vector::iterator& it, T& value) + { + value = fromString(*it); + ++it; + } +}; + +template +std::pair KVStore::getInternal(const std::string& key, std::pair*) +{ + std::vector strValues = getInternal(key, static_cast*>(nullptr)); + std::pair values; + + GetTupleVisitor visitor; + visitFields(values, &visitor, strValues.begin()); + + return values; +} + /** * Concatenates all parameters into one std::string. * Uses '.' to connect the terms. diff --git a/libs/config/to-fdstore-visitor.hpp b/libs/config/to-fdstore-visitor.hpp index 7ce8058..ba9bb8e 100644 --- a/libs/config/to-fdstore-visitor.hpp +++ b/libs/config/to-fdstore-visitor.hpp @@ -28,11 +28,14 @@ #include "config/is-visitable.hpp" #include "config/fdstore.hpp" #include "config/types.hpp" +#include "config/visit-fields.hpp" #include #include #include #include +#include + namespace config { @@ -105,6 +108,12 @@ private: } } + template + void writeInternal(const std::pair& values) + { + visitFields(values, this, std::string()); + } + }; } // namespace config diff --git a/libs/config/to-gvariant-visitor.hpp b/libs/config/to-gvariant-visitor.hpp index 1bd1375..09c04e6 100644 --- a/libs/config/to-gvariant-visitor.hpp +++ b/libs/config/to-gvariant-visitor.hpp @@ -28,11 +28,13 @@ #include "config/is-visitable.hpp" #include "config/is-union.hpp" #include "config/types.hpp" +#include "config/visit-fields.hpp" #include #include #include #include +#include namespace config { @@ -77,15 +79,33 @@ public: private: GVariantBuilder* mBuilder; - void writeInternal(std::int32_t value) { add("i", value); }; - void writeInternal(std::int64_t value) { add("x", value); }; - void writeInternal(std::uint32_t value) { add("u", value); }; - void writeInternal(std::uint64_t value) { add("t", value); }; - void writeInternal(bool value) { add("b", value); }; - void writeInternal(double value) { add("d", value); }; - void writeInternal(const std::string& value) { add("s", value.c_str()); }; - void writeInternal(const char* value) { add("s", value); }; - void writeInternal(const config::FileDescriptor& value) { add("h", value.value); }; + void writeInternal(std::int32_t value) { + add("i", value); + }; + void writeInternal(std::int64_t value) { + add("x", value); + }; + void writeInternal(std::uint32_t value) { + add("u", value); + }; + void writeInternal(std::uint64_t value) { + add("t", value); + }; + void writeInternal(bool value) { + add("b", value); + }; + void writeInternal(double value) { + add("d", value); + }; + void writeInternal(const std::string& value) { + add("s", value.c_str()); + }; + void writeInternal(const char* value) { + add("s", value); + }; + void writeInternal(const config::FileDescriptor& value) { + add("h", value.value); + }; template void writeInternal(const std::vector& value) @@ -115,6 +135,14 @@ private: } } + template + void writeInternal(const std::pair& values) + { + g_variant_builder_open(mBuilder, G_VARIANT_TYPE_ARRAY); + visitFields(values, this, std::string()); + g_variant_builder_close(mBuilder); + } + template typename std::enable_if::value && !isUnion::value>::type writeInternal(const T& value) diff --git a/libs/config/to-json-visitor.hpp b/libs/config/to-json-visitor.hpp index 3c746d2..3b8facf 100644 --- a/libs/config/to-json-visitor.hpp +++ b/libs/config/to-json-visitor.hpp @@ -27,10 +27,14 @@ #include "config/is-visitable.hpp" #include "config/exception.hpp" +#include "config/visit-fields.hpp" -#include +#include #include #include +#include + +#include namespace config { @@ -145,6 +149,25 @@ private: return array; } + struct HelperVisitor + { + template + static void visit(json_object* array, const T& value) + { + json_object_array_add(array, toJsonObject(value)); + } + }; + + template + static json_object* toJsonObject(const std::pair& values) + { + json_object* array = json_object_new_array(); + + HelperVisitor visitor; + visitFields(values, &visitor, array); + return array; + } + template::value>::type> static json_object* toJsonObject(const T& value) { diff --git a/libs/config/visit-fields.hpp b/libs/config/visit-fields.hpp new file mode 100644 index 0000000..1840c42 --- /dev/null +++ b/libs/config/visit-fields.hpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Jan Olszak (j.olszak@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 Jan Olszak (j.olszak@samsung.com) + * @brief Helper function for iterating tuples, pairs and arrays + */ + + +#ifndef CONFIG_VISIT_FIELDS_HPP +#define CONFIG_VISIT_FIELDS_HPP + +#include +#include + +namespace { + +template +struct visitImpl +{ + static void visit(T& t, F f, A&& ... args) + { + visitImpl::visit(t, f, std::forward(args)...); + f->visit(args..., std::get(t)); + } +}; + +template +struct visitImpl<0, T, F, A...> +{ + static void visit(T& t, F f, A&& ... args) + { + f->visit(args..., std::get<0>(t)); + } +}; + +} // namespace + +namespace config { + +template +void visitFields(T& t, F f, A ... args) +{ + visitImpl::value - 1, T, F, A...>::visit(t, f, std::forward(args)...); +} + +} // namespace config + +#endif // CONFIG_VISIT_FIELDS_HPP diff --git a/tests/unit_tests/config/testconfig-example.hpp b/tests/unit_tests/config/testconfig-example.hpp index c6f8476..3cdd876 100644 --- a/tests/unit_tests/config/testconfig-example.hpp +++ b/tests/unit_tests/config/testconfig-example.hpp @@ -99,6 +99,8 @@ struct TestConfig { std::array intArray; + std::pair intIntPair; + SubConfig subObj; std::vector subVector; @@ -124,6 +126,8 @@ struct TestConfig { intArray, + intIntPair, + subObj, subVector, @@ -164,16 +168,17 @@ const std::string jsonTestString = "\"stringVector\": [ \"a\", \"b\" ], " "\"doubleVector\": [ 0.000000, 1.000000, 2.000000 ], " "\"intArray\": [ 0, 1 ], " + "\"intIntPair\": [ 8, 9 ], " "\"subObj\": { \"intVal\": 54321, \"intVector\": [ 1, 2 ], \"subSubObj\": { \"intVal\": 234 } }, " "\"subVector\": [ { \"intVal\": 123, \"intVector\": [ 3, 4 ], \"subSubObj\": { \"intVal\": 345 } }, " - "{ \"intVal\": 456, \"intVector\": [ 5, 6 ], \"subSubObj\": { \"intVal\": 567 } } ], " + "{ \"intVal\": 456, \"intVector\": [ 5, 6 ], \"subSubObj\": { \"intVal\": 567 } } ], " "\"union1\": { \"type\": \"int\", \"value\": 2 }, " "\"union2\": { \"type\": \"SubConfig\", \"value\": { \"intVal\": 54321, \"intVector\": [ 1 ], " - "\"subSubObj\": { \"intVal\": 234 } } }, " + "\"subSubObj\": { \"intVal\": 234 } } }, " "\"unions\": [ " - "{ \"type\": \"int\", \"value\": 2 }, " - "{ \"type\": \"SubConfig\", \"value\": { \"intVal\": 54321, \"intVector\": [ 1 ], " - "\"subSubObj\": { \"intVal\": 234 } } } ] }"; + "{ \"type\": \"int\", \"value\": 2 }, " + "{ \"type\": \"SubConfig\", \"value\": { \"intVal\": 54321, \"intVector\": [ 1 ], " + "\"subSubObj\": { \"intVal\": 234 } } } ] }"; const std::string jsonEmptyTestString = "{ \"intVal\": 0, " @@ -188,6 +193,7 @@ const std::string jsonEmptyTestString = "\"stringVector\": [ ], " "\"doubleVector\": [ ], " "\"intArray\": [ ], " + "\"intIntPair\": [ ], " "\"subObj\": { \"intVal\": 0, \"intVector\": [ ], \"subSubObj\": { \"intVal\": 0 } }, " "\"subVector\": [ ], " "\"union1\": { \"type\": \"int\", \"value\": 0 }, " diff --git a/tests/unit_tests/config/ut-configuration.cpp b/tests/unit_tests/config/ut-configuration.cpp index a134b7b..e50ed92 100644 --- a/tests/unit_tests/config/ut-configuration.cpp +++ b/tests/unit_tests/config/ut-configuration.cpp @@ -88,6 +88,9 @@ BOOST_AUTO_TEST_CASE(FromJsonString) BOOST_CHECK_EQUAL(0, testConfig.intArray[0]); BOOST_CHECK_EQUAL(1, testConfig.intArray[1]); + BOOST_CHECK_EQUAL(8, testConfig.intIntPair.first); + BOOST_CHECK_EQUAL(9, testConfig.intIntPair.second); + BOOST_CHECK_EQUAL(54321, testConfig.subObj.intVal); BOOST_CHECK_EQUAL(2, testConfig.subObj.intVector.size()); BOOST_CHECK_EQUAL(1, testConfig.subObj.intVector[0]); -- 2.7.4 From d9e722e1469d27bd32b62baf10999f0c865401ab Mon Sep 17 00:00:00 2001 From: Pawel Kubik Date: Tue, 6 Oct 2015 16:06:40 +0200 Subject: [PATCH 04/16] Remove redundant gcc debug option from CMakeList.txt [Feature] N/A [Cause] N/A [Solution] N/A [Verification] Build Change-Id: I400ad9d6c91560d18d90a3d09d1ee656b4a31948 --- CMakeLists.txt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0409167..0bd22e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,14 +57,14 @@ else() SET(CXX_11_STD "c++11") endif() -SET(CMAKE_C_FLAGS_PROFILING "-g -O0 -pg") -SET(CMAKE_CXX_FLAGS_PROFILING "-g -std=${CXX_11_STD} -O0 -pg") -SET(CMAKE_C_FLAGS_DEBUG "-g -O0 -ggdb") -SET(CMAKE_CXX_FLAGS_DEBUG "-g -std=${CXX_11_STD} -O0 -ggdb") -SET(CMAKE_C_FLAGS_RELEASE "-g -O2 -DNDEBUG") -SET(CMAKE_CXX_FLAGS_RELEASE "-g -std=${CXX_11_STD} -O2 -DNDEBUG") -SET(CMAKE_C_FLAGS_CCOV "-g -O0 --coverage") -SET(CMAKE_CXX_FLAGS_CCOV "-g -std=${CXX_11_STD} -O0 --coverage") +SET(CMAKE_C_FLAGS_PROFILING "-O0 -pg") +SET(CMAKE_CXX_FLAGS_PROFILING "-std=${CXX_11_STD} -O0 -pg") +SET(CMAKE_C_FLAGS_DEBUG "-O0 -ggdb") +SET(CMAKE_CXX_FLAGS_DEBUG "-std=${CXX_11_STD} -O0 -ggdb") +SET(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG") +SET(CMAKE_CXX_FLAGS_RELEASE "-std=${CXX_11_STD} -O2 -DNDEBUG") +SET(CMAKE_C_FLAGS_CCOV "-O0 --coverage") +SET(CMAKE_CXX_FLAGS_CCOV "-std=${CXX_11_STD} -O0 --coverage") IF(DEFINED SANITIZE) # Enable sanitize build. -- 2.7.4 From 89486aaa187165127629f9d07b52d0d129132902 Mon Sep 17 00:00:00 2001 From: Pawel Kubik Date: Fri, 18 Sep 2015 15:03:33 +0200 Subject: [PATCH 05/16] IPC unit tests and testing framework improvements [Feature] 1. IPC unit tests 2. Cleaner args parsing in vsm_launch_test.py 3. Options to launch a test under custom tools [Cause] 1. N/A 2. Bugs when launching with external tool 3. Ability to launch any GDB front-end [Solution] 1. N/A 2. Use list instead of string in Popen 3. Two new command line options [Verification] Build, install run tests. Try to run single test with a fixture, for example: vsm_launch_test.py vasum-server-unit-tests -t \ 'IPCSuite/Restart<15ThreadedFixture>' To test running with --gdb option run test logged as root or use sudo with -E option in order to preserve environment. Change-Id: Icb09c0abed5c671c86a8c85d2aab1aa2b2412d29 --- common/utils/eventfd.cpp | 2 +- common/utils/exception.hpp | 5 ++ libs/ipc/exception.hpp | 19 +++++ libs/ipc/internals/processor.cpp | 2 +- libs/ipc/internals/socket.cpp | 4 +- libs/ipc/unique-id.cpp | 4 +- tests/scripts/vsm_launch_test.py | 32 +++---- tests/unit_tests/ipc/ut-ipc.cpp | 149 +++++++++++++++++++++++---------- tests/unit_tests/ipc/ut-unique-id.cpp | 19 +++++ tests/unit_tests/utils/ut-fd-utils.cpp | 51 +++++++++++ 10 files changed, 219 insertions(+), 68 deletions(-) create mode 100644 tests/unit_tests/utils/ut-fd-utils.cpp diff --git a/common/utils/eventfd.cpp b/common/utils/eventfd.cpp index bc5e64a..627b773 100644 --- a/common/utils/eventfd.cpp +++ b/common/utils/eventfd.cpp @@ -42,7 +42,7 @@ EventFD::EventFD() if (mFD == -1) { const std::string msg = "Error in eventfd: " + getSystemErrorMessage(); LOGE(msg); - throw UtilsException(msg); + throw EventFDException(msg); } } diff --git a/common/utils/exception.hpp b/common/utils/exception.hpp index 232b090..01bb380 100644 --- a/common/utils/exception.hpp +++ b/common/utils/exception.hpp @@ -39,6 +39,11 @@ struct UtilsException: public std::runtime_error { explicit UtilsException(const std::string& error) : std::runtime_error(error) {} }; +struct EventFDException: public UtilsException { + + explicit EventFDException(const std::string& error) : UtilsException(error) {} +}; + struct ProvisionExistsException: public UtilsException { explicit ProvisionExistsException(const std::string& error) : UtilsException(error) {} diff --git a/libs/ipc/exception.hpp b/libs/ipc/exception.hpp index 3fe7a5b..f757bcf 100644 --- a/libs/ipc/exception.hpp +++ b/libs/ipc/exception.hpp @@ -104,6 +104,25 @@ struct IPCTimeoutException: public IPCException { }; /** + * Exception to indicate socket error + * @ingroup IPCException + */ +struct IPCSocketException: public IPCException { + IPCSocketException(const int code, const std::string& message) + : IPCException(message), + mCode(code) + {} + + int getCode() const + { + return mCode; + } + +private: + int mCode; +}; + +/** * Exception to indicate user error * @ingroup IPCException */ diff --git a/libs/ipc/internals/processor.cpp b/libs/ipc/internals/processor.cpp index 164d735..8cfeed1 100644 --- a/libs/ipc/internals/processor.cpp +++ b/libs/ipc/internals/processor.cpp @@ -130,7 +130,7 @@ void Processor::stop(bool wait) mRequestQueue.pushBack(Event::FINISH, request); } - if(wait){ + if (wait) { LOGD(mLogPrefix + "Waiting for the Processor to stop"); // Wait till the FINISH request is served diff --git a/libs/ipc/internals/socket.cpp b/libs/ipc/internals/socket.cpp index a88ff0d..9bdfd35 100644 --- a/libs/ipc/internals/socket.cpp +++ b/libs/ipc/internals/socket.cpp @@ -150,7 +150,7 @@ int Socket::createSocketInternal(const std::string& path) if (sockfd == -1) { const std::string msg = "Error in socket: " + getSystemErrorMessage(); LOGE(msg); - throw IPCException(msg); + throw IPCSocketException(errno, msg); } setFdOptions(sockfd); @@ -210,7 +210,7 @@ Socket Socket::connectSocket(const std::string& path) if (fd == -1) { const std::string msg = "Error in socket: " + getSystemErrorMessage(); LOGE(msg); - throw IPCException(msg); + throw IPCSocketException(errno, msg); } setFdOptions(fd); diff --git a/libs/ipc/unique-id.cpp b/libs/ipc/unique-id.cpp index bcdd4bc..4a121ec 100644 --- a/libs/ipc/unique-id.cpp +++ b/libs/ipc/unique-id.cpp @@ -55,9 +55,7 @@ UniqueID::operator std::string() const std::ostream& operator<<(std::ostream& str, const UniqueID& id) { - char uuid[37]; - ::uuid_unparse(id.mUUID, uuid); - str << id.mTime.tv_sec << "." << id.mTime.tv_nsec << ":" << uuid; + str << static_cast(id); return str; } diff --git a/tests/scripts/vsm_launch_test.py b/tests/scripts/vsm_launch_test.py index dd5831f..b4fe529 100755 --- a/tests/scripts/vsm_launch_test.py +++ b/tests/scripts/vsm_launch_test.py @@ -39,20 +39,15 @@ def launchTest(cmd=[], externalToolCmd=[], parsing=True): if externalToolCmd and not _checkIfBinExists(externalToolCmd[0]): return - cmd[1:] = ["'{0}'".format(arg) if re.search("^\s*[^']*/.*<.*>\s*$", arg) - else arg - for arg in cmd[1:]] - log.info("Starting " + cmd[0] + " ...") if parsing: parser = Parser() - command = " ".join(externalToolCmd + cmd + _defLaunchArgs) - log.info("Invoking `" + command + "`") - p = subprocess.Popen(command, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + commandString = " ".join(externalToolCmd + cmd + _defLaunchArgs) + log.info("Invoking `" + commandString + "`") + p = subprocess.Popen(externalToolCmd + cmd + _defLaunchArgs, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) testResult = parser.parseOutputFromProcess(p) if testResult != "": domResult = minidom.parseString(testResult) @@ -63,10 +58,9 @@ def launchTest(cmd=[], externalToolCmd=[], parsing=True): else: # Launching process without coloring does not require report in XML form # Avoid providing --report_format=XML, redirect std* by default to system's std* - command = " ".join(externalToolCmd + cmd + _defLaunchArgs[1:]) - log.info("Invoking `" + command + "`") - p = subprocess.Popen(command, - shell=True) + commandString = " ".join(externalToolCmd + cmd + _defLaunchArgs[1:]) + log.info("Invoking `" + commandString + "`") + p = subprocess.Popen(externalToolCmd + cmd + _defLaunchArgs[1:]) p.wait() log.info(cmd[0] + " finished.") @@ -82,7 +76,8 @@ def main(): group.add_argument('--valgrind', action='store_true', help='Launch test binary inside Valgrind (assuming it is installed).') group.add_argument('--gdb', action='store_true', - help='Launch test binary with GDB (assuming it is installed).') + help='Launch test binary with a tool specified by $VSM_DEBUGGER variable. ' + +'Defaults to gdb.') argparser.add_argument('binary', nargs=argparse.REMAINDER, help='Binary to be launched using script.') @@ -90,7 +85,12 @@ def main(): if args[0].binary: if args[0].gdb: - launchTest(args[0].binary, externalToolCmd=_gdbCmd + args[1], parsing=False) + debuggerVar = os.getenv("VSM_DEBUGGER") + if (debuggerVar): + _customDebuggerCmd = debuggerVar.split() + else: + _customDebuggerCmd = _gdbCmd + launchTest(args[0].binary, externalToolCmd=_customDebuggerCmd + args[1], parsing=False) elif args[0].valgrind: launchTest(args[0].binary, externalToolCmd=_valgrindCmd + args[1]) else: diff --git a/tests/unit_tests/ipc/ut-ipc.cpp b/tests/unit_tests/ipc/ut-ipc.cpp index 5ba540e..0e16806 100644 --- a/tests/unit_tests/ipc/ut-ipc.cpp +++ b/tests/unit_tests/ipc/ut-ipc.cpp @@ -37,6 +37,7 @@ #include "ipc/result.hpp" #include "ipc/epoll/thread-dispatcher.hpp" #include "ipc/epoll/glib-dispatcher.hpp" +#include "utils/channel.hpp" #include "utils/glib-loop.hpp" #include "utils/latch.hpp" #include "utils/value-latch.hpp" @@ -53,8 +54,10 @@ #include #include #include +#include #include #include +#include #include @@ -207,7 +210,7 @@ void shortEchoCallback(const PeerID, methodResult->set(returnData); } -PeerID connect(Service& s, Client& c) +PeerID connectPeer(Service& s, Client& c) { // Connects the Client to the Service and returns Clients PeerID ValueLatch peerIDLatch; @@ -265,7 +268,7 @@ MULTI_FIXTURE_TEST_CASE(ServiceAddRemoveMethod, F, ThreadedFixture, GlibFixture) s.setMethodHandler(2, returnDataCallback); Client c(F::getPoll(), SOCKET_PATH); - connect(s, c); + connectPeer(s, c); testEcho(c, 1); s.removeMethod(1); @@ -281,7 +284,7 @@ MULTI_FIXTURE_TEST_CASE(ClientAddRemoveMethod, F, ThreadedFixture, GlibFixture) c.setMethodHandler(1, returnEmptyCallback); c.setMethodHandler(1, returnDataCallback); - PeerID peerID = connect(s, c); + PeerID peerID = connectPeer(s, c); c.setMethodHandler(1, echoCallback); c.setMethodHandler(2, returnDataCallback); @@ -294,6 +297,29 @@ MULTI_FIXTURE_TEST_CASE(ClientAddRemoveMethod, F, ThreadedFixture, GlibFixture) BOOST_CHECK_THROW(testEcho(s, 1, peerID), IPCException); } +MULTI_FIXTURE_TEST_CASE(MethodResultGetPeerID, F, ThreadedFixture, GlibFixture) +{ + Service s(F::getPoll(), SOCKET_PATH); + Client c(F::getPoll(), SOCKET_PATH); + + PeerID peerID = connectPeer(s, c); + + s.setMethodHandler( + 1, + [&peerID](const PeerID, + std::shared_ptr&, + MethodResult::Pointer methodResult) { + methodResult->setVoid(); + BOOST_CHECK_EQUAL(peerID, methodResult->getPeerID()); + } + ); + + std::shared_ptr sentData(new SendData(32)); + std::shared_ptr recvData = c.callSync(1, + sentData, + TIMEOUT); +} + MULTI_FIXTURE_TEST_CASE(ServiceStartStop, F, ThreadedFixture, GlibFixture) { Service s(F::getPoll(), SOCKET_PATH); @@ -334,7 +360,7 @@ MULTI_FIXTURE_TEST_CASE(SyncClientToServiceEcho, F, ThreadedFixture, GlibFixture s.setMethodHandler(2, echoCallback); Client c(F::getPoll(), SOCKET_PATH); - connect(s, c); + connectPeer(s, c); testEcho(c, 1); testEcho(c, 2); @@ -375,7 +401,7 @@ MULTI_FIXTURE_TEST_CASE(SyncServiceToClientEcho, F, ThreadedFixture, GlibFixture Service s(F::getPoll(), SOCKET_PATH); Client c(F::getPoll(), SOCKET_PATH); c.setMethodHandler(1, echoCallback); - PeerID peerID = connect(s, c); + PeerID peerID = connectPeer(s, c); std::shared_ptr sentData(new SendData(56)); std::shared_ptr recvData = s.callSync(1, peerID, sentData); @@ -414,7 +440,7 @@ MULTI_FIXTURE_TEST_CASE(AsyncServiceToClientEcho, F, ThreadedFixture, GlibFixtur Service s(F::getPoll(), SOCKET_PATH); Client c(F::getPoll(), SOCKET_PATH); c.setMethodHandler(1, echoCallback); - PeerID peerID = connect(s, c); + PeerID peerID = connectPeer(s, c); // Async call auto dataBack = [&recvDataLatch](Result && r) { @@ -435,7 +461,7 @@ MULTI_FIXTURE_TEST_CASE(SyncTimeout, F, ThreadedFixture, GlibFixture) s.setMethodHandler(1, longEchoCallback); Client c(F::getPoll(), SOCKET_PATH); - connect(s, c); + connectPeer(s, c); std::shared_ptr sentData(new SendData(78)); BOOST_REQUIRE_THROW((c.callSync(1, sentData, TIMEOUT)), IPCException); @@ -447,7 +473,7 @@ MULTI_FIXTURE_TEST_CASE(SerializationError, F, ThreadedFixture, GlibFixture) s.setMethodHandler(1, echoCallback); Client c(F::getPoll(), SOCKET_PATH); - connect(s, c); + connectPeer(s, c); std::shared_ptr throwingData(new ThrowOnAcceptData()); @@ -513,7 +539,7 @@ MULTI_FIXTURE_TEST_CASE(ReadTimeout, F, ThreadedFixture, GlibFixture) s.setMethodHandler(1, longEchoCallback); Client c(F::getPoll(), SOCKET_PATH); - connect(s, c); + connectPeer(s, c); // Test timeout on read std::shared_ptr sentData(new SendData(334)); @@ -549,7 +575,7 @@ MULTI_FIXTURE_TEST_CASE(AddSignalInRuntime, F, ThreadedFixture, GlibFixture) Service s(F::getPoll(), SOCKET_PATH); Client c(F::getPoll(), SOCKET_PATH); - connect(s, c); + connectPeer(s, c); auto handlerA = [&recvDataLatchA](const PeerID, std::shared_ptr& data) { recvDataLatchA.set(data); @@ -597,7 +623,7 @@ MULTI_FIXTURE_TEST_CASE(AddSignalOffline, F, ThreadedFixture, GlibFixture) c.setSignalHandler(1, handlerA); c.setSignalHandler(2, handlerB); - connect(s, c); + connectPeer(s, c); // Wait for the information about the signals to propagate std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT)); @@ -621,7 +647,7 @@ MULTI_FIXTURE_TEST_CASE(UsersError, F, ThreadedFixture, GlibFixture) Service s(F::getPoll(), SOCKET_PATH); Client c(F::getPoll(), SOCKET_PATH); - auto clientID = connect(s, c); + auto clientID = connectPeer(s, c); auto throwingMethodHandler = [&](const PeerID, std::shared_ptr&, MethodResult::Pointer) { throw IPCUserException(TEST_ERROR_CODE, TEST_ERROR_MESSAGE); @@ -655,7 +681,7 @@ MULTI_FIXTURE_TEST_CASE(AsyncResult, F, ThreadedFixture, GlibFixture) Service s(F::getPoll(), SOCKET_PATH); Client c(F::getPoll(), SOCKET_PATH); - auto clientID = connect(s, c); + auto clientID = connectPeer(s, c); auto errorMethodHandler = [&](const PeerID, std::shared_ptr&, MethodResult::Pointer methodResult) { std::async(std::launch::async, [&, methodResult] { @@ -718,7 +744,7 @@ MULTI_FIXTURE_TEST_CASE(MixOperations, F, ThreadedFixture, GlibFixture) Client c(F::getPoll(), SOCKET_PATH); s.setSignalHandler(2, signalHandler); - connect(s, c); + connectPeer(s, c); testEcho(c, 1); @@ -749,7 +775,7 @@ MULTI_FIXTURE_TEST_CASE(FDSendReceive, F, ThreadedFixture, GlibFixture) s.setMethodHandler(1, methodHandler); Client c(F::getPoll(), SOCKET_PATH); - connect(s, c); + connectPeer(s, c); std::shared_ptr fdData; std::shared_ptr sentData(new EmptyData()); @@ -762,36 +788,69 @@ MULTI_FIXTURE_TEST_CASE(FDSendReceive, F, ThreadedFixture, GlibFixture) ::close(fdData->fd.value); } -// MULTI_FIXTURE_TEST_CASE(ConnectionLimit, F, ThreadedFixture, GlibFixture) -// { -// unsigned oldLimit = ipc::getMaxFDNumber(); -// ipc::setMaxFDNumber(50); - -// // Setup Service and many Clients -// Service s(F::getPoll(), SOCKET_PATH); -// s.setMethodHandler(1, echoCallback); -// s.start(); - -// std::list clients; -// for (int i = 0; i < 100; ++i) { -// try { -// clients.push_back(Client(F::getPoll(), SOCKET_PATH)); -// clients.back().start(); -// } catch (...) {} -// } - -// unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); -// std::mt19937 generator(seed); -// for (auto it = clients.begin(); it != clients.end(); ++it) { -// try { -// std::shared_ptr sentData(new SendData(generator())); -// std::shared_ptr recvData = it->callSync(1, sentData); -// BOOST_CHECK_EQUAL(recvData->intVal, sentData->intVal); -// } catch (...) {} -// } - -// ipc::setMaxFDNumber(oldLimit); -// } +BOOST_AUTO_TEST_CASE(ConnectionLimit) +{ + const unsigned oldLimit = utils::getMaxFDNumber(); + const unsigned newLimit = 32; + ScopedDir scopedDir(TEST_DIR); + + Channel c; + + const pid_t chpid = ::fork(); + BOOST_CHECK_NE(chpid, -1); + + if (chpid) { + // Setup Service + ThreadDispatcher td; + Service s(td.getPoll(), SOCKET_PATH); + s.setMethodHandler(1, echoCallback); + s.start(); + + c.setLeft(); + try { + // inform the Client + c.write(true); + } catch (...) { + kill(chpid, 9); + throw; + } + + int status; + BOOST_CHECK_EQUAL(::waitpid(chpid, &status, 0), chpid); + BOOST_CHECK_EQUAL(status, EXIT_SUCCESS); + } else { + int ret = EXIT_FAILURE; + utils::setMaxFDNumber(newLimit); + + c.setRight(); + try { + // wait for the Service + c.read(); + } catch(...) { + ::_exit(EXIT_FAILURE); + } + + // Setup Clients + ThreadDispatcher td; + std::list clients; + try { + for (unsigned i = 0; i < 2 * newLimit; ++i) { + clients.emplace_back(td.getPoll(), SOCKET_PATH); + clients.back().start(); + } + } catch (const EventFDException& e) { + ret = EXIT_SUCCESS; + } catch (const IPCSocketException& e) { + if (e.getCode() == EMFILE) { + ret = EXIT_SUCCESS; + } + } + + utils::setMaxFDNumber(oldLimit); + + ::_exit(ret); + } +} diff --git a/tests/unit_tests/ipc/ut-unique-id.cpp b/tests/unit_tests/ipc/ut-unique-id.cpp index 05ce1bb..e60bc88 100644 --- a/tests/unit_tests/ipc/ut-unique-id.cpp +++ b/tests/unit_tests/ipc/ut-unique-id.cpp @@ -29,6 +29,7 @@ #include "ipc/unique-id.hpp" #include +#include #include namespace { @@ -70,4 +71,22 @@ BOOST_AUTO_TEST_CASE(DoubleGenerate) BOOST_CHECK_NE(uid1, uid2); } +// compare two empty UIDs +BOOST_AUTO_TEST_CASE(EmptyCompare) +{ + ipc::UniqueID uid1, uid2; + + BOOST_CHECK_EQUAL(uid1, uid2); +} + +// pass empty UID to a stream +BOOST_AUTO_TEST_CASE(StreamOperator) +{ + ipc::UniqueID uid; + std::stringstream ss; + + ss << uid; + BOOST_CHECK_EQUAL(ss.str(), "0.0:" + EMPTY_UUID); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/unit_tests/utils/ut-fd-utils.cpp b/tests/unit_tests/utils/ut-fd-utils.cpp new file mode 100644 index 0000000..3c0962d --- /dev/null +++ b/tests/unit_tests/utils/ut-fd-utils.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Pawel Kubik (p.kubik@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 Pawel Kubik (p.kubik@samsung.com) + * @brief Unit tests of fd utils + */ + +#include "config.hpp" +#include "ut.hpp" + +#include "utils/fd-utils.hpp" + +#include "logger/logger.hpp" + + +using namespace utils; + + +BOOST_AUTO_TEST_SUITE(FDUtilsSuite) + +BOOST_AUTO_TEST_CASE(GetSetMaxFDNumber) +{ + unsigned oldLimit = utils::getMaxFDNumber(); + unsigned newLimit = 50; + + utils::setMaxFDNumber(newLimit); + BOOST_CHECK_EQUAL(newLimit, utils::getMaxFDNumber()); + + utils::setMaxFDNumber(oldLimit); + BOOST_CHECK_EQUAL(oldLimit, utils::getMaxFDNumber()); +} + +BOOST_AUTO_TEST_SUITE_END() -- 2.7.4 From 95bde33bc81d699e404fc1d3ffe378ec98b5f259 Mon Sep 17 00:00:00 2001 From: Lukasz Pawelczyk Date: Fri, 9 Oct 2015 16:37:59 +0200 Subject: [PATCH 06/16] Fix the color codes effectively fixing the BOLD colors [Feature] N/A [Cause] All the colors were always bold [Solution] Use proper ANSII color codes [Verification] Visual Change-Id: Id8be7fbbdad46e9952fcecfa2bdd8328eb4e7164 --- common/utils/ccolor.hpp | 16 ++++++++-------- tests/scripts/vsm_test_parser.py | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/common/utils/ccolor.hpp b/common/utils/ccolor.hpp index 1700967..b579354 100644 --- a/common/utils/ccolor.hpp +++ b/common/utils/ccolor.hpp @@ -31,14 +31,14 @@ namespace utils { enum class Color : unsigned int { DEFAULT = 0, - BLACK = 90, - RED = 91, - GREEN = 92, - YELLOW = 93, - BLUE = 94, - MAGENTA = 95, - CYAN = 96, - WHITE = 97 + BLACK = 30, + RED = 31, + GREEN = 32, + YELLOW = 33, + BLUE = 34, + MAGENTA = 35, + CYAN = 36, + WHITE = 37 }; enum class Attributes : unsigned int { diff --git a/tests/scripts/vsm_test_parser.py b/tests/scripts/vsm_test_parser.py index fc0eae6..ee63a55 100644 --- a/tests/scripts/vsm_test_parser.py +++ b/tests/scripts/vsm_test_parser.py @@ -3,14 +3,14 @@ import sys -BLACK = "\033[90m" -RED = "\033[91m" -GREEN = "\033[92m" -YELLOW = "\033[93m" -BLUE = "\033[94m" -MAGENTA = "\033[95m" -CYAN = "\033[96m" -WHITE = "\033[97m" +BLACK = "\033[30m" +RED = "\033[31m" +GREEN = "\033[32m" +YELLOW = "\033[33m" +BLUE = "\033[34m" +MAGENTA = "\033[35m" +CYAN = "\033[36m" +WHITE = "\033[37m" BOLD = "\033[1m" ENDC = "\033[0m" -- 2.7.4 From 2f62c1f25d9a9a8360f3d671ade64b61dd857f97 Mon Sep 17 00:00:00 2001 From: Krzysztof Dynowski Date: Thu, 8 Oct 2015 14:03:43 +0200 Subject: [PATCH 07/16] lxcpp: cgroups API [Feature] Control-groups API for containers [Cause] N/A [Solution] N/A [Verification] N/A Change-Id: I69605383b40e3b3e1a8c2f6942e85023b367728e --- libs/lxcpp/CMakeLists.txt | 3 +- libs/lxcpp/cgroups/cgroup.cpp | 26 +++++++++++++ libs/lxcpp/cgroups/cgroup.hpp | 83 ++++++++++++++++++++++++++++++++++++++++ libs/lxcpp/cgroups/subsystem.hpp | 81 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 libs/lxcpp/cgroups/cgroup.cpp create mode 100644 libs/lxcpp/cgroups/cgroup.hpp create mode 100644 libs/lxcpp/cgroups/subsystem.hpp diff --git a/libs/lxcpp/CMakeLists.txt b/libs/lxcpp/CMakeLists.txt index 9e49843..0d35bad 100644 --- a/libs/lxcpp/CMakeLists.txt +++ b/libs/lxcpp/CMakeLists.txt @@ -33,13 +33,14 @@ FILE(GLOB HEADERS_COMMANDS commands/*.hpp) FILE(GLOB SRCS *.cpp *.hpp) FILE(GLOB SRCS_COMMANDS commands/*.cpp) +FILE(GLOB SRCS_CGROUPS cgroups/*.cpp) SET(_LIB_VERSION_ "${VERSION}") SET(_LIB_SOVERSION_ "0") SET(PC_FILE "lib${PROJECT_NAME}.pc") ## Setup target ################################################################ -ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS} ${SRCS_COMMANDS}) +ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS} ${SRCS_COMMANDS} ${SRCS_CGROUPS}) SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES SOVERSION ${_LIB_SOVERSION_} VERSION ${_LIB_VERSION_} diff --git a/libs/lxcpp/cgroups/cgroup.cpp b/libs/lxcpp/cgroups/cgroup.cpp new file mode 100644 index 0000000..574af61 --- /dev/null +++ b/libs/lxcpp/cgroups/cgroup.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Krzysztof Dynowski (k.dynowski@samsumg.com) + * @brief Control-groups management + */ + +#include "lxcpp/cgroups/cgroup.hpp" + +// added this file now, to make hpp go through compilation diff --git a/libs/lxcpp/cgroups/cgroup.hpp b/libs/lxcpp/cgroups/cgroup.hpp new file mode 100644 index 0000000..b47268f --- /dev/null +++ b/libs/lxcpp/cgroups/cgroup.hpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Krzysztof Dynowski (k.dynowski@samsumg.com) + * @brief Control-groups management + */ + +#ifndef LXCPP_CGROUPS_CGROUP_HPP +#define LXCPP_CGROUPS_CGROUP_HPP + +#include "lxcpp/cgroups/subsystem.hpp" + +class CGroup { + +public: + /** + * Define control-group object + */ + CGroup(const Subsystem& subsys, const std::string& name) : + mSubsys(subsys), + mName(name) + { + } + + /** + * Check if cgroup exists + * @return true if cgroup path (subsys.path / mName) exists + */ + bool exists(); + + /** + * Create cgroup directory + * Equivalent of: mkdir subsys.path / mName + */ + void create(); + + /** + * Destroy cgroup directory + * Equivalent of: rmdir subsys.path / mName + * Note: set memory.force_empty before removing a cgroup to avoid moving out-of-use page caches to parent + */ + void destroy(); + + /** + * Set cgroup parameter to value (name validity depends on subsystem) + * Equivalent of: echo value > mSubsys_path/mName/mSubsys_name.param + */ + void setValue(const std::string& param, const std::string& value); + + /** + * Get cgroup parameter + * Equivalent of: cat mSubsys_path/mName/mSubsys_name.param + */ + std::string getValue(const std::string& key); + + /** + * Move process to this cgroup (process can't be removed from a cgroup) + * Equivalent of: echo pid > mSubsys_path/mName/tasks + */ + void moveProcess(pid_t pid); + +private: + const Subsystem& mSubsys; // referred subsystem + const std::string& mName; // path relative to subsystem "root" +}; + +#endif // LXCPP_CGROUPS_CGROUP_HPP diff --git a/libs/lxcpp/cgroups/subsystem.hpp b/libs/lxcpp/cgroups/subsystem.hpp new file mode 100644 index 0000000..bb9887e --- /dev/null +++ b/libs/lxcpp/cgroups/subsystem.hpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Krzysztof Dynowski (k.dynowski@samsumg.com) + * @brief Control-groups management + */ + +#ifndef LXCPP_CGROUPS_SUBSYSTEM_HPP +#define LXCPP_CGROUPS_SUBSYSTEM_HPP + +#include + +#include +#include + +class Subsystem { +public: + /** + * Define subsystem object + */ + Subsystem(const std::string& name); + + /** + * Check if named subsystem is supported by the kernel + * @return true if subsystem is listed in /proc/cgroups + */ + bool isAvailable(); + + /** + * Check if named subsystem is mounted (added to hierarchy) + * @return true if subsystem has a mount point (as read from /proc/mounts) + */ + bool isAttached(); + + /** + * Attach subsystem hierarchy to filesystem + * Equivalent of: mount -t cgroup -o subs(coma-sep) subs(underln-sep) path + */ + static void attach(const std::string& path, std::vector subs); + + /** + * Detach subsstem hierarchy from filesystem + * Equivalent of: umount path + */ + static void detach(const std::string& path); + + /** + * Get list of available subsytems + * @return parse contents of /proc/cgroups + */ + static std::vector availableSubsystems(); + + /** + * Get control groups list for a process (in format subsys_name:cgroup_name) + * eg. "cpu:/user/test_user" + * Equivalent of: cat /proc/pid/cgroup + */ + static std::vector getCGroups(pid_t pid); + +private: + const std::string& mName; + std::string mPath; +}; + +#endif // LXCPP_CGROUPS_SUBSYSTEM_HPP -- 2.7.4 From 64b5dbb4b7ae47002e35e05765637c04c526b0b6 Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Wed, 7 Oct 2015 18:13:52 +0200 Subject: [PATCH 08/16] config: enum class serialization [Feature] Serialization of strongly typed enums [Cause] N/A [Solution] N/A [Verification] Build, install, run tests. Change-Id: Ie4d112995cff8d58c0cc6fad6c0646ec03d5aa83 --- libs/config/from-fdstore-visitor.hpp | 6 ++++++ libs/config/from-gvariant-visitor.hpp | 7 +++++++ libs/config/from-json-visitor.hpp | 9 ++++++++- libs/config/from-kvjson-visitor.hpp | 11 ++++++++++- libs/config/kvstore.hpp | 26 ++++++++++++++++++++++---- libs/config/to-fdstore-visitor.hpp | 6 ++++++ libs/config/to-gvariant-visitor.hpp | 6 ++++++ libs/config/to-json-visitor.hpp | 9 ++++++++- tests/unit_tests/config/testconfig-example.hpp | 9 +++++++++ tests/unit_tests/config/ut-configuration.cpp | 1 + 10 files changed, 83 insertions(+), 7 deletions(-) diff --git a/libs/config/from-fdstore-visitor.hpp b/libs/config/from-fdstore-visitor.hpp index f0c8dbe..8b7d082 100644 --- a/libs/config/from-fdstore-visitor.hpp +++ b/libs/config/from-fdstore-visitor.hpp @@ -93,6 +93,12 @@ private: value.accept(visitor); } + template::value, int>::type = 0> + void readInternal(T& value) + { + readInternal(*reinterpret_cast::type*>(&value)); + } + template void readInternal(std::vector& values) { diff --git a/libs/config/from-gvariant-visitor.hpp b/libs/config/from-gvariant-visitor.hpp index afc1beb..78e001d 100644 --- a/libs/config/from-gvariant-visitor.hpp +++ b/libs/config/from-gvariant-visitor.hpp @@ -200,6 +200,13 @@ private: visitFields(values, &visitor, &iter); } + template::value, int>::type = 0> + static void fromGVariant(GVariant* object, T& value) + { + fromGVariant(object, + *reinterpret_cast::type*>(&value)); + } + template static typename std::enable_if::value>::type fromGVariant(GVariant* object, T& value) diff --git a/libs/config/from-json-visitor.hpp b/libs/config/from-json-visitor.hpp index 9bdf19b..b599084 100644 --- a/libs/config/from-json-visitor.hpp +++ b/libs/config/from-json-visitor.hpp @@ -192,7 +192,14 @@ private: visitFields(values, &visitor, object, idx); } - template::value>::type> + template::value, int>::type = 0> + static void fromJsonObject(json_object* object, T& value) + { + fromJsonObject(object, + *reinterpret_cast::type*>(&value)); + } + + template::value, int>::type = 0> static void fromJsonObject(json_object* object, T& value) { checkType(object, json_type_object); diff --git a/libs/config/from-kvjson-visitor.hpp b/libs/config/from-kvjson-visitor.hpp index d9dbce7..e66b90d 100644 --- a/libs/config/from-kvjson-visitor.hpp +++ b/libs/config/from-kvjson-visitor.hpp @@ -107,7 +107,16 @@ private: mObject = object ? json_object_get(object) : nullptr; } - template::value, int>::type = 0> + template::value, int>::type = 0> + void getValue(const std::string& name, T& t) + { + getValue(name, + *reinterpret_cast::type*>(&t)); + } + + template::value && + !std::is_enum::value, int>::type = 0> void getValue(const std::string& name, T& t) { std::string k = key(mKeyPrefix, name); diff --git a/libs/config/kvstore.hpp b/libs/config/kvstore.hpp index 1206d08..40aa199 100644 --- a/libs/config/kvstore.hpp +++ b/libs/config/kvstore.hpp @@ -139,7 +139,9 @@ private: void setInternal(const std::string& key, const char* value); void setInternal(const std::string& key, const std::initializer_list& values); void setInternal(const std::string& key, const std::vector& values); - template + template::value, int>::type = 0> + void setInternal(const std::string& key, const T& value); + template::value, int>::type = 0> void setInternal(const std::string& key, const T& value); template void setInternal(const std::string& key, const std::vector& values); @@ -151,7 +153,9 @@ private: std::string getInternal(const std::string& key, std::string*); char* getInternal(const std::string& key, char**); std::vector getInternal(const std::string& key, std::vector*); - template + template::value, int>::type = 0> + T getInternal(const std::string& key, T*); + template::value, int>::type = 0> T getInternal(const std::string& key, T*); template std::vector getInternal(const std::string& key, std::vector*); @@ -195,12 +199,19 @@ T fromString(const std::string& strValue) } // namespace -template +template::value, int>::type> void KVStore::setInternal(const std::string& key, const T& value) { setInternal(key, toString(value)); } +template::value, int>::type> +void KVStore::setInternal(const std::string& key, const T& value) +{ + setInternal(key, + static_cast::type>(value)); +} + template void KVStore::setInternal(const std::string& key, const std::vector& values) { @@ -247,12 +258,19 @@ void KVStore::setInternal(const std::string& key, const std::pair& values) setInternal(key, strValues); } -template +template::value, int>::type> T KVStore::getInternal(const std::string& key, T*) { return fromString(getInternal(key, static_cast(nullptr))); } +template::value, int>::type> +T KVStore::getInternal(const std::string& key, T*) +{ + return static_cast(getInternal(key, + static_cast::type*>(nullptr))); +} + template std::array KVStore::getInternal(const std::string& key, std::array*) { diff --git a/libs/config/to-fdstore-visitor.hpp b/libs/config/to-fdstore-visitor.hpp index ba9bb8e..b3e6b84 100644 --- a/libs/config/to-fdstore-visitor.hpp +++ b/libs/config/to-fdstore-visitor.hpp @@ -78,6 +78,12 @@ private: mStore.sendFD(fd.value); } + template::value, int>::type = 0> + void writeInternal(const T& value) + { + writeInternal(static_cast::type>(value)); + } + template::value, int>::type = 0> void writeInternal(const T& value) { diff --git a/libs/config/to-gvariant-visitor.hpp b/libs/config/to-gvariant-visitor.hpp index 09c04e6..c0df2da 100644 --- a/libs/config/to-gvariant-visitor.hpp +++ b/libs/config/to-gvariant-visitor.hpp @@ -143,6 +143,12 @@ private: g_variant_builder_close(mBuilder); } + template::value, int>::type = 0> + void writeInternal(const T& value) + { + writeInternal(static_cast::type>(value)); + } + template typename std::enable_if::value && !isUnion::value>::type writeInternal(const T& value) diff --git a/libs/config/to-json-visitor.hpp b/libs/config/to-json-visitor.hpp index 3b8facf..ba0d6ed 100644 --- a/libs/config/to-json-visitor.hpp +++ b/libs/config/to-json-visitor.hpp @@ -168,13 +168,20 @@ private: return array; } - template::value>::type> + template::value, int>::type = 0> static json_object* toJsonObject(const T& value) { ToJsonVisitor visitor; value.accept(visitor); return visitor.detach(); } + + template::value, int>::type = 0> + static json_object* toJsonObject(const T& value) + { + return toJsonObject(static_cast::type>(value)); + } + }; } // namespace config diff --git a/tests/unit_tests/config/testconfig-example.hpp b/tests/unit_tests/config/testconfig-example.hpp index 3cdd876..0ddd975 100644 --- a/tests/unit_tests/config/testconfig-example.hpp +++ b/tests/unit_tests/config/testconfig-example.hpp @@ -29,6 +29,11 @@ #include "config/fields.hpp" #include "config/fields-union.hpp" +enum class TestEnum: int { + FIRST = 0, + SECOND = 12 +}; + struct TestConfig { // subtree class struct SubConfig { @@ -91,6 +96,7 @@ struct TestConfig { char* cstringVal; double doubleVal; bool boolVal; + TestEnum enumVal; std::vector emptyIntVector; std::vector intVector; @@ -118,6 +124,7 @@ struct TestConfig { cstringVal, doubleVal, boolVal, + enumVal, emptyIntVector, intVector, @@ -163,6 +170,7 @@ const std::string jsonTestString = "\"cstringVal\": \"blah\", " "\"doubleVal\": -1.234000, " "\"boolVal\": true, " + "\"enumVal\": 12, " "\"emptyIntVector\": [ ], " "\"intVector\": [ 1, 2, 3 ], " "\"stringVector\": [ \"a\", \"b\" ], " @@ -188,6 +196,7 @@ const std::string jsonEmptyTestString = "\"stringVal\": \"\", " "\"cstringVal\": \"\", " "\"boolVal\": false, " + "\"enumVal\": 0, " "\"emptyIntVector\": [ ], " "\"intVector\": [ ], " "\"stringVector\": [ ], " diff --git a/tests/unit_tests/config/ut-configuration.cpp b/tests/unit_tests/config/ut-configuration.cpp index e50ed92..ab1a200 100644 --- a/tests/unit_tests/config/ut-configuration.cpp +++ b/tests/unit_tests/config/ut-configuration.cpp @@ -67,6 +67,7 @@ BOOST_AUTO_TEST_CASE(FromJsonString) BOOST_CHECK_EQUAL("blah", testConfig.cstringVal); BOOST_CHECK_CLOSE(-1.234, testConfig.doubleVal, TOLERANCE); BOOST_CHECK_EQUAL(true, testConfig.boolVal); + BOOST_CHECK(TestEnum::SECOND == testConfig.enumVal); BOOST_REQUIRE_EQUAL(0, testConfig.emptyIntVector.size()); -- 2.7.4 From 9f5816c9a34f40b6acfc8924ee914d5165c1e35f Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Thu, 8 Oct 2015 14:19:22 +0200 Subject: [PATCH 09/16] lxcpp: Switch on serialization from libConfig [Feature] Use std::pair and enum serialization [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: I6edb4543bf85eacfb2efe13065970326b30aba17 --- libs/lxcpp/attach/attach-config.hpp | 14 ++++++-------- libs/lxcpp/attach/attach-helper.cpp | 4 ++-- libs/lxcpp/commands/attach.cpp | 2 +- libs/lxcpp/commands/attach.hpp | 2 +- libs/lxcpp/container-impl.cpp | 2 +- libs/lxcpp/container-impl.hpp | 2 +- libs/lxcpp/container.hpp | 2 +- libs/lxcpp/guard/guard.cpp | 11 +---------- libs/lxcpp/process.cpp | 16 ++++++++++++++++ libs/lxcpp/process.hpp | 2 ++ 10 files changed, 32 insertions(+), 25 deletions(-) diff --git a/libs/lxcpp/attach/attach-config.hpp b/libs/lxcpp/attach/attach-config.hpp index 9788abe..94e2626 100644 --- a/libs/lxcpp/attach/attach-config.hpp +++ b/libs/lxcpp/attach/attach-config.hpp @@ -39,7 +39,7 @@ namespace lxcpp { struct AttachConfig { /// Arguments passed by user, argv[0] is the binary's path in container - std::vector argv; + std::vector argv; /// PID of the container's init process pid_t initPid; @@ -73,7 +73,7 @@ struct AttachConfig { AttachConfig() = default; - AttachConfig(const std::vector& argv, + AttachConfig(const std::vector& argv, const pid_t initPid, const std::vector& namespaces, const uid_t uid, @@ -99,19 +99,17 @@ struct AttachConfig { CONFIG_REGISTER ( //TODO: Uncomment and fix cstring serialization - // argv, + argv, initPid, - //TODO: Uncomment and fix Namespace serialization (or remove Namespace) - // namespaces, + namespaces, uid, gid, ttyFD, supplementaryGids, capsToKeep, workDirInContainer, - envToKeep - //TODO: Uncomment and fix std::pair serialization - // envToSet + envToKeep, + envToSet ) }; diff --git a/libs/lxcpp/attach/attach-helper.cpp b/libs/lxcpp/attach/attach-helper.cpp index 321502f..5aaaecb 100644 --- a/libs/lxcpp/attach/attach-helper.cpp +++ b/libs/lxcpp/attach/attach-helper.cpp @@ -70,8 +70,8 @@ int child(void* data) ::_exit(EXIT_FAILURE); } - // Run user's binary - ::execve(config.argv[0], const_cast(config.argv.data()), nullptr); + lxcpp::execve(config.argv); + return EXIT_FAILURE; } diff --git a/libs/lxcpp/commands/attach.cpp b/libs/lxcpp/commands/attach.cpp index 2bfa592..a059986 100644 --- a/libs/lxcpp/commands/attach.cpp +++ b/libs/lxcpp/commands/attach.cpp @@ -34,7 +34,7 @@ namespace lxcpp { Attach::Attach(const lxcpp::ContainerImpl& container, - const std::vector& argv, + const std::vector& argv, const uid_t uid, const gid_t gid, const std::string& ttyPath, diff --git a/libs/lxcpp/commands/attach.hpp b/libs/lxcpp/commands/attach.hpp index ce39b83..123a384 100644 --- a/libs/lxcpp/commands/attach.hpp +++ b/libs/lxcpp/commands/attach.hpp @@ -56,7 +56,7 @@ public: * @param envToSet new environment variables that will be set */ Attach(const lxcpp::ContainerImpl& container, - const std::vector& argv, + const std::vector& argv, const uid_t uid, const gid_t gid, const std::string& ttyPath, diff --git a/libs/lxcpp/container-impl.cpp b/libs/lxcpp/container-impl.cpp index c7035e4..9ff4b52 100644 --- a/libs/lxcpp/container-impl.cpp +++ b/libs/lxcpp/container-impl.cpp @@ -205,7 +205,7 @@ void ContainerImpl::reboot() throw NotImplementedException(); } -void ContainerImpl::attach(const std::vector& argv, +void ContainerImpl::attach(const std::vector& argv, const std::string& cwdInContainer) { Attach attach(*this, diff --git a/libs/lxcpp/container-impl.hpp b/libs/lxcpp/container-impl.hpp index 7393974..041303b 100644 --- a/libs/lxcpp/container-impl.hpp +++ b/libs/lxcpp/container-impl.hpp @@ -65,7 +65,7 @@ public: void reboot(); // Other - void attach(const std::vector& argv, + void attach(const std::vector& argv, const std::string& cwdInContainer); // Network interfaces setup/config diff --git a/libs/lxcpp/container.hpp b/libs/lxcpp/container.hpp index 51dda8c..7f98aed 100644 --- a/libs/lxcpp/container.hpp +++ b/libs/lxcpp/container.hpp @@ -71,7 +71,7 @@ public: virtual void reboot() = 0; // Other - virtual void attach(const std::vector& argv, + virtual void attach(const std::vector& argv, const std::string& cwdInContainer) = 0; // Network interfaces setup/config diff --git a/libs/lxcpp/guard/guard.cpp b/libs/lxcpp/guard/guard.cpp index 952cdf5..369d423 100644 --- a/libs/lxcpp/guard/guard.cpp +++ b/libs/lxcpp/guard/guard.cpp @@ -38,16 +38,7 @@ namespace lxcpp { void startContainer(const ContainerConfig &cfg) { - std::vector argv; - argv.reserve(cfg.mInit.size() + 1); - for (auto const & it : cfg.mInit) { - argv.push_back(it.c_str()); - } - argv.push_back(nullptr); - - LOGD("Executing container's init: " << argv[0]); - ::execve(argv[0], const_cast(argv.data()), NULL); - ::_exit(EXIT_FAILURE); + lxcpp::execve(cfg.mInit); } int startGuard(int channelFD) diff --git a/libs/lxcpp/process.cpp b/libs/lxcpp/process.cpp index 0353d82..204dec2 100644 --- a/libs/lxcpp/process.cpp +++ b/libs/lxcpp/process.cpp @@ -156,4 +156,20 @@ void unshare(const Namespace ns) throw ProcessSetupException(msg); } } + +void execve(const std::vector& argv) +{ + // Prepare the arguments + std::vector tmpArgv; + tmpArgv.reserve(argv.size() + 1); + + for (auto const &str : argv) { + tmpArgv.push_back(str.c_str()); + } + tmpArgv.push_back(nullptr); + + // Run user's binary + ::execve(tmpArgv[0], const_cast(tmpArgv.data()), nullptr); +} + } // namespace lxcpp diff --git a/libs/lxcpp/process.hpp b/libs/lxcpp/process.hpp index 869e3d6..75c1e4f 100644 --- a/libs/lxcpp/process.hpp +++ b/libs/lxcpp/process.hpp @@ -49,6 +49,8 @@ int waitpid(const pid_t pid); void unshare(const Namespace ns); +void execve(const std::vector& argv); + } // namespace lxcpp #endif // LXCPP_PROCESS_HPP \ No newline at end of file -- 2.7.4 From 3b6129779f3829bd1a1987087ccdd7d14a4af98d Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Thu, 8 Oct 2015 17:26:23 +0200 Subject: [PATCH 10/16] lxcpp: Added simple container tests [Feature] setInit and setLogger tests [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: I944c76aa4483c6c287a6b032b4d7456ab58f7d44 --- libs/lxcpp/container.hpp | 2 +- libs/lxcpp/guard/guard.cpp | 2 +- tests/unit_tests/lxcpp/ut-container.cpp | 75 ++++++++++++++++++++++++++++++++- 3 files changed, 75 insertions(+), 4 deletions(-) diff --git a/libs/lxcpp/container.hpp b/libs/lxcpp/container.hpp index 7f98aed..e3d537f 100644 --- a/libs/lxcpp/container.hpp +++ b/libs/lxcpp/container.hpp @@ -59,7 +59,7 @@ public: virtual void setLogger(const logger::LogType type, const logger::LogLevel level, - const std::string &arg) = 0; + const std::string &arg = "") = 0; virtual void setTerminalCount(const unsigned int count) = 0; diff --git a/libs/lxcpp/guard/guard.cpp b/libs/lxcpp/guard/guard.cpp index 369d423..28e572a 100644 --- a/libs/lxcpp/guard/guard.cpp +++ b/libs/lxcpp/guard/guard.cpp @@ -60,7 +60,7 @@ int startGuard(int channelFD) setProcTitle(title); } catch (std::exception &e) { // Ignore, this is optional - LOGW("Failed to set the guard process title"); + LOGW("Failed to set the guard process title: " << e.what()); } // TODO: container preparation part 1 diff --git a/tests/unit_tests/lxcpp/ut-container.cpp b/tests/unit_tests/lxcpp/ut-container.cpp index 35672e0..ff72b94 100644 --- a/tests/unit_tests/lxcpp/ut-container.cpp +++ b/tests/unit_tests/lxcpp/ut-container.cpp @@ -29,10 +29,29 @@ #include "lxcpp/lxcpp.hpp" #include "lxcpp/exception.hpp" +#include "utils/scoped-dir.hpp" + +#include + namespace { +const std::string TEST_DIR = "/tmp/ut-zones"; +const std::string ROOT_DIR = TEST_DIR + "/root"; +const std::string NON_EXISTANT_BINARY = TEST_DIR + "/nonexistantpath/bash"; +const std::string LOGGER_FILE = TEST_DIR + "/loggerFile"; + +const std::vector COMMAND = {"/bin/bash", + "-c", "trap exit SIGTERM; while true; do sleep 0.1; done" + }; + struct Fixture { - Fixture() {} + utils::ScopedDir mTestDir; + utils::ScopedDir mRoot; + + Fixture() + :mTestDir(TEST_DIR), + mRoot(ROOT_DIR) + {} ~Fixture() {} }; @@ -44,8 +63,60 @@ using namespace lxcpp; BOOST_AUTO_TEST_CASE(ConstructorDestructor) { - auto c = createContainer("FirstTestContainer", "/"); + auto c = createContainer("ConstructorDestructor", ROOT_DIR); delete c; } +BOOST_AUTO_TEST_CASE(SetInit) +{ + auto c = std::unique_ptr(createContainer("SetInit", "/")); + + BOOST_CHECK_THROW(c->setInit({""}), ConfigureException); + BOOST_CHECK_THROW(c->setInit({}), ConfigureException); + BOOST_CHECK_THROW(c->setInit({NON_EXISTANT_BINARY}), ConfigureException); + + BOOST_CHECK_NO_THROW(c->setInit(COMMAND)); +} + +BOOST_AUTO_TEST_CASE(SetLogger) +{ + auto c = std::unique_ptr(createContainer("SetLogger", ROOT_DIR)); + + BOOST_CHECK_NO_THROW(c->setLogger(logger::LogType::LOG_NULL, + logger::LogLevel::DEBUG)); + BOOST_CHECK_NO_THROW(c->setLogger(logger::LogType::LOG_JOURNALD, + logger::LogLevel::DEBUG)); + BOOST_CHECK_NO_THROW(c->setLogger(logger::LogType::LOG_SYSLOG, + logger::LogLevel::DEBUG)); + BOOST_CHECK_NO_THROW(c->setLogger(logger::LogType::LOG_STDERR, + logger::LogLevel::DEBUG)); + + BOOST_CHECK_NO_THROW(c->setLogger(logger::LogType::LOG_FILE, + logger::LogLevel::DEBUG, + LOGGER_FILE)); + BOOST_CHECK_NO_THROW(c->setLogger(logger::LogType::LOG_PERSISTENT_FILE, + logger::LogLevel::DEBUG, + LOGGER_FILE)); + + BOOST_CHECK_THROW(c->setLogger(logger::LogType::LOG_FILE, + logger::LogLevel::DEBUG, + ""), + BadArgument); + + BOOST_CHECK_THROW(c->setLogger(logger::LogType::LOG_PERSISTENT_FILE, + logger::LogLevel::DEBUG, + ""), + BadArgument); +} + +// BOOST_AUTO_TEST_CASE(StartStop) +// { +// auto c = std::unique_ptr(createContainer("StartStop", "/")); +// BOOST_CHECK_NO_THROW(c->setInit(COMMAND)); +// BOOST_CHECK_NO_THROW(c->setLogger(logger::LogType::LOG_PERSISTENT_FILE, +// logger::LogLevel::DEBUG, +// LOGGER_FILE)); +// BOOST_CHECK_NO_THROW(c->start()); +// } + BOOST_AUTO_TEST_SUITE_END() -- 2.7.4 From 94f4fbe894f1b987af9cb4183f14f8c5c43010be Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Mon, 12 Oct 2015 12:43:10 +0200 Subject: [PATCH 11/16] lxcpp: Stopping container's init process [Feature] Terminating container's init process [Cause] N/A [Solution] N/A [Verification] Build, install and run tests Change-Id: I77af77016d06555b6eea3e133e25a44574580c67 --- common/utils/signal.cpp | 11 +++++++ common/utils/signal.hpp | 1 + libs/lxcpp/commands/stop.cpp | 51 ++++++++++++++++++++++++++++++ libs/lxcpp/commands/stop.hpp | 56 +++++++++++++++++++++++++++++++++ libs/lxcpp/container-impl.cpp | 4 ++- libs/lxcpp/utils.cpp | 4 +-- tests/unit_tests/lxcpp/ut-container.cpp | 19 +++++------ 7 files changed, 134 insertions(+), 12 deletions(-) create mode 100644 libs/lxcpp/commands/stop.cpp create mode 100644 libs/lxcpp/commands/stop.hpp diff --git a/common/utils/signal.cpp b/common/utils/signal.cpp index bad3b0b..51c45b1 100644 --- a/common/utils/signal.cpp +++ b/common/utils/signal.cpp @@ -133,6 +133,17 @@ void signalIgnore(const std::initializer_list& signals) } } +void sendSignal(const pid_t pid, const int sigNum) +{ + if (-1 == ::kill(pid, sigNum)) { + const std::string msg = "Error during killing pid: " + std::to_string(pid) + + " sigNum: " + std::to_string(sigNum) + + ": " + getSystemErrorMessage(); + LOGE(msg); + throw UtilsException(msg); + } +} + } // namespace utils diff --git a/common/utils/signal.hpp b/common/utils/signal.hpp index 3ecf166..f4a42e3 100644 --- a/common/utils/signal.hpp +++ b/common/utils/signal.hpp @@ -36,6 +36,7 @@ void signalBlockAllExcept(const std::initializer_list& signals); void signalBlock(const int sigNum); void signalUnblock(const int sigNum); void signalIgnore(const std::initializer_list& signals); +void sendSignal(const pid_t pid, const int sigNum); } // namespace utils diff --git a/libs/lxcpp/commands/stop.cpp b/libs/lxcpp/commands/stop.cpp new file mode 100644 index 0000000..111ef9f --- /dev/null +++ b/libs/lxcpp/commands/stop.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief Implementation of stopping a container + */ + +#include "lxcpp/commands/stop.hpp" +#include "lxcpp/exception.hpp" +#include "lxcpp/process.hpp" + +#include "logger/logger.hpp" +#include "utils/signal.hpp" + +namespace lxcpp { + +Stop::Stop(ContainerConfig &config) + : mConfig(config) +{ +} + +Stop::~Stop() +{ +} + +void Stop::execute() +{ + LOGD("Stopping container: " << mConfig.mName); + + // TODO: Use initctl/systemd-initctl if available in container + + utils::sendSignal(mConfig.mInitPid, SIGTERM); +} + +} // namespace lxcpp diff --git a/libs/lxcpp/commands/stop.hpp b/libs/lxcpp/commands/stop.hpp new file mode 100644 index 0000000..291a0cd --- /dev/null +++ b/libs/lxcpp/commands/stop.hpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief Implementation of stopping a container + */ + +#ifndef LXCPP_COMMANDS_STOP_HPP +#define LXCPP_COMMANDS_STOP_HPP + +#include "lxcpp/commands/command.hpp" +#include "lxcpp/container-config.hpp" + +#include + + +namespace lxcpp { + + +class Stop final: Command { +public: + /** + * Stops the container + * + * @param config container's config + */ + Stop(ContainerConfig &config); + ~Stop(); + + void execute(); + +private: + ContainerConfig &mConfig; +}; + + +} // namespace lxcpp + + +#endif // LXCPP_COMMANDS_STOP_HPP diff --git a/libs/lxcpp/container-impl.cpp b/libs/lxcpp/container-impl.cpp index 9ff4b52..d3258aa 100644 --- a/libs/lxcpp/container-impl.cpp +++ b/libs/lxcpp/container-impl.cpp @@ -29,6 +29,7 @@ #include "lxcpp/capability.hpp" #include "lxcpp/commands/attach.hpp" #include "lxcpp/commands/start.hpp" +#include "lxcpp/commands/stop.hpp" #include "lxcpp/commands/prep-host-terminal.hpp" #include "logger/logger.hpp" @@ -187,7 +188,8 @@ void ContainerImpl::stop() // TODO: things to do when shuttting down the container: // - close PTY master FDs from the config so we won't keep PTYs open - throw NotImplementedException(); + Stop stop(mConfig); + stop.execute(); } void ContainerImpl::freeze() diff --git a/libs/lxcpp/utils.cpp b/libs/lxcpp/utils.cpp index 0c731b5..d0418af 100644 --- a/libs/lxcpp/utils.cpp +++ b/libs/lxcpp/utils.cpp @@ -54,8 +54,8 @@ void setProcTitle(const std::string &title) // Skip the first 47 fields, entries 48-49 are ARG_START and ARG_END. std::advance(it, 47); - unsigned long argStart = std::stol(*it++); - unsigned long argEnd = std::stol(*it++); + unsigned long argStart = std::stoul(*it++); + unsigned long argEnd = std::stoul(*it); f.close(); diff --git a/tests/unit_tests/lxcpp/ut-container.cpp b/tests/unit_tests/lxcpp/ut-container.cpp index ff72b94..1c1a76c 100644 --- a/tests/unit_tests/lxcpp/ut-container.cpp +++ b/tests/unit_tests/lxcpp/ut-container.cpp @@ -109,14 +109,15 @@ BOOST_AUTO_TEST_CASE(SetLogger) BadArgument); } -// BOOST_AUTO_TEST_CASE(StartStop) -// { -// auto c = std::unique_ptr(createContainer("StartStop", "/")); -// BOOST_CHECK_NO_THROW(c->setInit(COMMAND)); -// BOOST_CHECK_NO_THROW(c->setLogger(logger::LogType::LOG_PERSISTENT_FILE, -// logger::LogLevel::DEBUG, -// LOGGER_FILE)); -// BOOST_CHECK_NO_THROW(c->start()); -// } +BOOST_AUTO_TEST_CASE(StartStop) +{ + auto c = std::unique_ptr(createContainer("StartStop", "/")); + BOOST_CHECK_NO_THROW(c->setInit(COMMAND)); + BOOST_CHECK_NO_THROW(c->setLogger(logger::LogType::LOG_PERSISTENT_FILE, + logger::LogLevel::DEBUG, + LOGGER_FILE)); + BOOST_CHECK_NO_THROW(c->start()); + BOOST_CHECK_NO_THROW(c->stop()); +} BOOST_AUTO_TEST_SUITE_END() -- 2.7.4 From 046432b8d6840b9d70891caaab2c5155229e2f77 Mon Sep 17 00:00:00 2001 From: "Maciej J. Karpiuk" Date: Mon, 12 Oct 2015 12:29:54 +0200 Subject: [PATCH 12/16] vasum: housekeeping, removal of 'using namespace std;' [Feature] N/A [Cause] N/A [Solution] N/A [Verification] N/A Change-Id: I583b5ad44cd0d460c6b0c4db0d05217ab05cc2e4 --- cli/command-line-interface.cpp | 126 ++++++++++++++++++++--------------------- client/vasum-client-impl.cpp | 43 +++++++------- client/vasum-client-impl.hpp | 2 +- client/vasum-client.cpp | 4 +- server/netdev.cpp | 69 +++++++++++----------- 5 files changed, 119 insertions(+), 125 deletions(-) diff --git a/cli/command-line-interface.cpp b/cli/command-line-interface.cpp index 27d7cac..ad5400d 100644 --- a/cli/command-line-interface.cpp +++ b/cli/command-line-interface.cpp @@ -44,8 +44,6 @@ #include #include -using namespace std; - namespace vasum { namespace cli { @@ -92,23 +90,23 @@ std::string netdevToString(const VsmNetdev& netdev) return out; } -typedef vector> Table; +typedef std::vector> Table; -ostream& operator<<(ostream& out, const Table& table) +std::ostream& operator<<(std::ostream& out, const Table& table) { - vector sizes; + std::vector sizes; for (const auto& row : table) { if (sizes.size() < row.size()) { sizes.resize(row.size()); } for (size_t i = 0; i < row.size(); ++i) { - sizes[i] = max(sizes[i], row[i].length()); + sizes[i] = std::max(sizes[i], row[i].length()); } } for (const auto& row : table) { for (size_t i = 0; i < row.size(); ++i) { - out << left << setw(sizes[i]+2) << row[i]; + out << std::left << std::setw(sizes[i]+2) << row[i]; } out << "\n"; } @@ -129,7 +127,7 @@ enum macvlan_mode macvlanFromString(const std::string& mode) { if (mode == "passthru") { return MACVLAN_MODE_PASSTHRU; } - throw runtime_error("Unsupported macvlan mode"); + throw std::runtime_error("Unsupported macvlan mode"); } void buildZoneList(std::vector& list) @@ -144,7 +142,7 @@ void buildZoneList(std::vector& list) vsm_array_string_free(ids); } -void buildNetdevList(const std::string& zone,std::vector& list) +void buildNetdevList(const std::string& zone, std::vector& list) { using namespace std::placeholders; VsmArrayString ids; @@ -167,9 +165,9 @@ const std::vector CommandLineInterface::buildCompletionList(const A } ArgSpec as = mArgsSpec[a.size() - 2]; - string::size_type s = 0U; - string::size_type e = s; - while (e != string::npos) { + std::string::size_type s = 0U; + std::string::size_type e = s; + while (e != std::string::npos) { e = as.format.find('|', s); std::string ss = as.format.substr(s, e - s); s = e + 1; @@ -197,12 +195,12 @@ void CommandLineInterface::connect() CommandLineInterface::client = vsm_client_create(); if (CommandLineInterface::client == nullptr) { - throw runtime_error("Can't create client"); + throw std::runtime_error("Can't create client"); } status = vsm_connect(client); if (VSMCLIENT_SUCCESS != status) { - string msg = vsm_get_status_message(CommandLineInterface::client); + std::string msg = vsm_get_status_message(CommandLineInterface::client); vsm_client_free(CommandLineInterface::client); CommandLineInterface::client = nullptr; throw IOException(msg); @@ -211,7 +209,7 @@ void CommandLineInterface::connect() void CommandLineInterface::disconnect() { - string msg; + std::string msg; VsmStatus status; if (CommandLineInterface::client == nullptr) { @@ -227,17 +225,17 @@ void CommandLineInterface::disconnect() CommandLineInterface::client = nullptr; if (VSMCLIENT_SUCCESS != status) { - throw runtime_error(msg); + throw std::runtime_error(msg); } } -void CommandLineInterface::executeCallback(const function& fun) +void CommandLineInterface::executeCallback(const std::function& fun) { CommandLineInterface::connect(); VsmStatus status = fun(CommandLineInterface::client); if (VSMCLIENT_SUCCESS != status) { - throw runtime_error(vsm_get_status_message(CommandLineInterface::client)); + throw std::runtime_error(vsm_get_status_message(CommandLineInterface::client)); } } @@ -306,7 +304,7 @@ void set_active_zone(const Args& argv) using namespace std::placeholders; if (argv.size() < 2) { - throw runtime_error("Not enough parameters"); + throw std::runtime_error("Not enough parameters"); } CommandLineInterface::executeCallback(bind(vsm_set_active_zone, _1, argv[1].c_str())); @@ -317,7 +315,7 @@ void create_zone(const Args& argv) using namespace std::placeholders; if (argv.size() < 2) { - throw runtime_error("Not enough parameters"); + throw std::runtime_error("Not enough parameters"); } if (argv.size() >= 3 && !argv[2].empty()) { @@ -332,7 +330,7 @@ void destroy_zone(const Args& argv) using namespace std::placeholders; if (argv.size() < 2) { - throw runtime_error("Not enough parameters"); + throw std::runtime_error("Not enough parameters"); } CommandLineInterface::executeCallback(bind(vsm_destroy_zone, _1, argv[1].c_str(), 1)); @@ -343,7 +341,7 @@ void shutdown_zone(const Args& argv) using namespace std::placeholders; if (argv.size() < 2) { - throw runtime_error("Not enough parameters"); + throw std::runtime_error("Not enough parameters"); } CommandLineInterface::executeCallback(bind(vsm_shutdown_zone, _1, argv[1].c_str())); @@ -354,7 +352,7 @@ void start_zone(const Args& argv) using namespace std::placeholders; if (argv.size() < 2) { - throw runtime_error("Not enough parameters"); + throw std::runtime_error("Not enough parameters"); } CommandLineInterface::executeCallback(bind(vsm_start_zone, _1, argv[1].c_str())); @@ -365,7 +363,7 @@ void console_zone(const Args& argv) using namespace std::placeholders; if (argv.size() < 2) { - throw runtime_error("Not enough parameters"); + throw std::runtime_error("Not enough parameters"); } VsmZone zone; @@ -373,7 +371,7 @@ void console_zone(const Args& argv) if (zoneStateToString(vsm_zone_get_state(zone)) != "RUNNING") { vsm_zone_free(zone); - throw runtime_error("Zone '" + argv[1] + "' is not running"); + throw std::runtime_error("Zone '" + argv[1] + "' is not running"); } std::string zonesPath = vsm_zone_get_rootfs(zone); @@ -389,7 +387,7 @@ void console_zone(const Args& argv) .add("-P").add(zonesPath.c_str()); if (!execv("/usr/bin/lxc-console", const_cast(args.c_array()))) { - throw runtime_error("Could not log into zone"); + throw std::runtime_error("Could not log into zone"); } } @@ -398,7 +396,7 @@ void lock_zone(const Args& argv) using namespace std::placeholders; if (argv.size() < 2) { - throw runtime_error("Not enough parameters"); + throw std::runtime_error("Not enough parameters"); } CommandLineInterface::executeCallback(bind(vsm_lock_zone, _1, argv[1].c_str())); @@ -409,7 +407,7 @@ void unlock_zone(const Args& argv) using namespace std::placeholders; if (argv.size() < 2) { - throw runtime_error("Not enough parameters"); + throw std::runtime_error("Not enough parameters"); } CommandLineInterface::executeCallback(bind(vsm_unlock_zone, _1, argv[1].c_str())); @@ -439,17 +437,17 @@ void get_zones_status(const Args& argv) for (VsmString* id = ids; *id; ++id) { VsmZone zone; CommandLineInterface::executeCallback(bind(vsm_lookup_zone_by_id, _1, *id, &zone)); - assert(string(vsm_zone_get_id(zone)) == string(*id)); - table.push_back({string(vsm_zone_get_id(zone)) == string(activeId) ? "YES" : "NO", + assert(std::string(vsm_zone_get_id(zone)) == std::string(*id)); + table.push_back({std::string(vsm_zone_get_id(zone)) == std::string(activeId) ? "YES" : "NO", vsm_zone_get_id(zone), zoneStateToString(vsm_zone_get_state(zone)), - to_string(vsm_zone_get_terminal(zone)), + std::to_string(vsm_zone_get_terminal(zone)), vsm_zone_get_rootfs(zone)}); vsm_zone_free(zone); } vsm_string_free(activeId); vsm_array_string_free(ids); - cout << table << endl; + std::cout << table << std::endl; } void get_zone_ids(const Args& /*argv*/) @@ -458,12 +456,12 @@ void get_zone_ids(const Args& /*argv*/) VsmArrayString ids; CommandLineInterface::executeCallback(bind(vsm_get_zone_ids, _1, &ids)); - string delim; + std::string delim; for (VsmString* id = ids; *id; ++id) { - cout << delim << *id; + std::cout << delim << *id; delim = ", "; } - cout << endl; + std::cout << std::endl; vsm_array_string_free(ids); } @@ -473,7 +471,7 @@ void get_active_zone(const Args& /*argv*/) VsmString id; CommandLineInterface::executeCallback(bind(vsm_get_active_zone_id, _1, &id)); - cout << id << endl; + std::cout << id << std::endl; vsm_string_free(id); } @@ -482,7 +480,7 @@ void grant_device(const Args& argv) using namespace std::placeholders; if (argv.size() < 3) { - throw runtime_error("Not enough parameters"); + throw std::runtime_error("Not enough parameters"); } uint32_t flags = O_RDWR; @@ -494,7 +492,7 @@ void revoke_device(const Args& argv) using namespace std::placeholders; if (argv.size() < 3) { - throw runtime_error("Not enough parameters"); + throw std::runtime_error("Not enough parameters"); } CommandLineInterface::executeCallback(bind(vsm_revoke_device, _1, argv[1].c_str(), argv[2].c_str())); @@ -505,13 +503,13 @@ void create_netdev(const Args& argv) using namespace std::placeholders; if (argv.size() < 3) { - throw runtime_error("Not enough parameters"); + throw std::runtime_error("Not enough parameters"); } std::string nettype = argv[2]; if (nettype == "phys") { if (argv.size() < 4) { - throw runtime_error("Not enough parameters"); + throw std::runtime_error("Not enough parameters"); } CommandLineInterface::executeCallback(bind(vsm_create_netdev_phys, _1, @@ -520,7 +518,7 @@ void create_netdev(const Args& argv) } else if (nettype == "veth") { if (argv.size() < 5) { - throw runtime_error("Not enough parameters"); + throw std::runtime_error("Not enough parameters"); } CommandLineInterface::executeCallback(bind(vsm_create_netdev_veth, _1, @@ -530,7 +528,7 @@ void create_netdev(const Args& argv) } else if (nettype == "macvlan") { if (argv.size() < 6) { - throw runtime_error("Not enough parameters"); + throw std::runtime_error("Not enough parameters"); } CommandLineInterface::executeCallback(bind(vsm_create_netdev_macvlan, _1, @@ -540,7 +538,7 @@ void create_netdev(const Args& argv) macvlanFromString(argv[5].c_str()))); } else - throw runtime_error("Wrong nettype option " + nettype); + throw std::runtime_error("Wrong nettype option " + nettype); } void destroy_netdev(const Args& argv) @@ -548,7 +546,7 @@ void destroy_netdev(const Args& argv) using namespace std::placeholders; if (argv.size() < 3) { - throw runtime_error("Not enough parameters"); + throw std::runtime_error("Not enough parameters"); } CommandLineInterface::executeCallback(bind(vsm_destroy_netdev, _1, @@ -561,7 +559,7 @@ void netdev_list(const Args& argv) using namespace std::placeholders; if (argv.size() < 2) { - throw runtime_error("Not enough parameters"); + throw std::runtime_error("Not enough parameters"); } if (argv.size() < 3) { VsmArrayString ids; @@ -569,15 +567,15 @@ void netdev_list(const Args& argv) _1, argv[1].c_str(), &ids)); - string delim; + std::string delim; for (VsmString* id = ids; *id; ++id) { - cout << delim << *id; + std::cout << delim << *id; delim = ", "; } if (delim.empty()) { - cout << "There is no network device in zone"; + std::cout << "There is no network device in zone"; } - cout << endl; + std::cout << std::endl; vsm_array_string_free(ids); } else { @@ -588,7 +586,7 @@ void netdev_list(const Args& argv) argv[1].c_str(), argv[2].c_str(), &netdev)); - cout << netdevToString(netdev) << endl; + std::cout << netdevToString(netdev) << std::endl; vsm_netdev_free(netdev); VsmAddrList addrs = NULL; @@ -601,9 +599,9 @@ void netdev_list(const Args& argv) for (unsigned i=0; i < listsize; ++i) { int type=vsm_addrlist_get_type(addrs, i); if (inet_ntop(type, vsm_addrlist_get_addr(addrs, i), buf, INET6_ADDRSTRLEN) == NULL) { - throw runtime_error("Wrong address received ["+std::to_string(i)+"] type="+std::to_string(type)); + throw std::runtime_error("Wrong address received ["+std::to_string(i)+"] type="+std::to_string(type)); } - cout << buf << "/" << vsm_addrlist_get_prefix(addrs, i) << endl; + std::cout << buf << "/" << vsm_addrlist_get_prefix(addrs, i) << std::endl; } vsm_addrlist_free(addrs); } @@ -613,31 +611,31 @@ void netdev_add_ip_addr(const Args& argv) { using namespace std::placeholders; if (argv.size() < 5) { - throw runtime_error("Not enough parameters"); + throw std::runtime_error("Not enough parameters"); } if (argv[3].find(':') == std::string::npos) { in_addr addr; if (inet_pton(AF_INET, argv[3].c_str(), &addr) != 1) { - throw runtime_error("Wrong address format"); + throw std::runtime_error("Wrong address format"); }; CommandLineInterface::executeCallback(bind(vsm_netdev_add_ipv4_addr, _1, argv[1].c_str(), argv[2].c_str(), &addr, - stoi(argv[4].c_str()))); + std::stoi(argv[4].c_str()))); } else { in6_addr addr; if (inet_pton(AF_INET6, argv[3].c_str(), &addr) != 1) { - throw runtime_error("Wrong address format"); + throw std::runtime_error("Wrong address format"); }; CommandLineInterface::executeCallback(bind(vsm_netdev_add_ipv6_addr, _1, argv[1].c_str(), argv[2].c_str(), &addr, - stoi(argv[4].c_str()))); + std::stoi(argv[4].c_str()))); } } @@ -645,31 +643,31 @@ void netdev_del_ip_addr(const Args& argv) { using namespace std::placeholders; if (argv.size() < 5) { - throw runtime_error("Not enough parameters"); + throw std::runtime_error("Not enough parameters"); } if (argv[3].find(':') == std::string::npos) { in_addr addr; if (inet_pton(AF_INET, argv[3].c_str(), &addr) != 1) { - throw runtime_error("Wrong address format"); + throw std::runtime_error("Wrong address format"); }; CommandLineInterface::executeCallback(bind(vsm_netdev_del_ipv4_addr, _1, argv[1].c_str(), argv[2].c_str(), &addr, - stoi(argv[4].c_str()))); + std::stoi(argv[4].c_str()))); } else { in6_addr addr; if (inet_pton(AF_INET6, argv[3].c_str(), &addr) != 1) { - throw runtime_error("Wrong address format"); + throw std::runtime_error("Wrong address format"); }; CommandLineInterface::executeCallback(bind(vsm_netdev_del_ipv6_addr, _1, argv[1].c_str(), argv[2].c_str(), &addr, - stoi(argv[4].c_str()))); + std::stoi(argv[4].c_str()))); } } @@ -678,7 +676,7 @@ void netdev_up(const Args& argv) using namespace std::placeholders; if (argv.size() < 3) { - throw runtime_error("Not enough parameters"); + throw std::runtime_error("Not enough parameters"); } CommandLineInterface::executeCallback(bind(vsm_netdev_up, _1, @@ -691,7 +689,7 @@ void netdev_down(const Args& argv) using namespace std::placeholders; if (argv.size() < 3) { - throw runtime_error("Not enough parameters"); + throw std::runtime_error("Not enough parameters"); } CommandLineInterface::executeCallback(bind(vsm_netdev_down, _1, diff --git a/client/vasum-client-impl.cpp b/client/vasum-client-impl.cpp index 41cc919..30cc74c 100644 --- a/client/vasum-client-impl.cpp +++ b/client/vasum-client-impl.cpp @@ -49,7 +49,6 @@ #include #include -using namespace std; using namespace utils; using namespace vasum; @@ -103,7 +102,7 @@ void convert(const api::ZoneInfoOut& info, Zone& zone) zone = vsmZone; } -string toString(const in_addr* addr) +std::string toString(const in_addr* addr) { char buf[INET_ADDRSTRLEN]; const char* ret = inet_ntop(AF_INET, addr, buf, INET_ADDRSTRLEN); @@ -113,7 +112,7 @@ string toString(const in_addr* addr) return ret; } -string toString(const in6_addr* addr) +std::string toString(const in6_addr* addr) { char buf[INET6_ADDRSTRLEN]; const char* ret = inet_ntop(AF_INET6, addr, buf, INET6_ADDRSTRLEN); @@ -123,9 +122,9 @@ string toString(const in6_addr* addr) return ret; } -bool readFirstLineOfFile(const string& path, string& ret) +bool readFirstLineOfFile(const std::string& path, std::string& ret) { - ifstream file(path); + std::ifstream file(path); if (!file) { return false; } @@ -146,7 +145,7 @@ Client::Status::Status() { } -Client::Status::Status(VsmStatus status, const string& msg) +Client::Status::Status(VsmStatus status, const std::string& msg) : mVsmStatus(status), mMsg(msg) { } @@ -182,7 +181,7 @@ ipc::epoll::EventPoll& Client::getEventPoll() const } } -VsmStatus Client::coverException(const function& worker) noexcept +VsmStatus Client::coverException(const std::function& worker) noexcept { try { worker(); @@ -202,7 +201,7 @@ VsmStatus Client::coverException(const function& worker) noexcept mStatus = Status(VSMCLIENT_CUSTOM_ERROR, ex.what()); } catch (const ipc::IPCException& ex) { mStatus = Status(VSMCLIENT_IO_ERROR, ex.what()); - } catch (const exception& ex) { + } catch (const std::exception& ex) { mStatus = Status(VSMCLIENT_CUSTOM_ERROR, ex.what()); } VsmStatus ret = mStatus.mVsmStatus; @@ -295,7 +294,7 @@ const char* Client::vsm_get_status_message() const noexcept VsmStatus Client::vsm_get_status() const noexcept { - lock_guard lock(mStatusMutex); + std::lock_guard lock(mStatusMutex); return mStatus.mVsmStatus; } @@ -354,14 +353,14 @@ VsmStatus Client::vsm_lookup_zone_by_pid(int pid, VsmString* id) noexcept return coverException([&] { IS_SET(id); - const string path = "/proc/" + to_string(pid) + "/cpuset"; + const std::string path = "/proc/" + std::to_string(pid) + "/cpuset"; - string cpuset; + std::string cpuset; if (!readFirstLineOfFile(path, cpuset)) { throw InvalidArgumentException("Process not found"); } - string zoneId; + std::string zoneId; if (!parseZoneIdFromCpuSet(cpuset, zoneId)) { throw OperationFailedException("unknown format of cpuset"); } @@ -407,7 +406,7 @@ VsmStatus Client::vsm_create_zone(const char* id, const char* tname) noexcept return coverException([&] { IS_SET(id); - string template_name = tname ? tname : "default"; + std::string template_name = tname ? tname : "default"; mClient->callSync( api::ipc::METHOD_CREATE_ZONE, std::make_shared(api::CreateZoneIn{ id, template_name }), @@ -552,7 +551,7 @@ VsmStatus Client::vsm_netdev_get_ip_addr(const char* id, std::vector addrAttrs; for(const auto& addrAttr : split(addrAttrs, attr.second, is_any_of(","))) { size_t pos = addrAttr.find(":"); - if (pos == string::npos) continue; + if (pos == std::string::npos) continue; if (addrAttr.substr(0, pos) == "prefixlen") { addr.prefix = atoi(addrAttr.substr(pos + 1).c_str()); @@ -610,7 +609,7 @@ VsmStatus Client::vsm_netdev_add_ipv4_addr(const char* id, IS_SET(netdevId); IS_SET(addr); - string value = "ip:" + toString(addr) + ",""prefixlen:" + to_string(prefix); + std::string value = "ip:" + toString(addr) + ",""prefixlen:" + std::to_string(prefix); mClient->callSync( api::ipc::METHOD_SET_NETDEV_ATTRS, std::make_shared( @@ -628,7 +627,7 @@ VsmStatus Client::vsm_netdev_add_ipv6_addr(const char* id, IS_SET(netdevId); IS_SET(addr); - string value = "ip:" + toString(addr) + ",""prefixlen:" + to_string(prefix); + std::string value = "ip:" + toString(addr) + ",""prefixlen:" + std::to_string(prefix); mClient->callSync( api::ipc::METHOD_SET_NETDEV_ATTRS, std::make_shared( @@ -647,7 +646,7 @@ VsmStatus Client::vsm_netdev_del_ipv4_addr(const char* id, IS_SET(addr); //CIDR notation - string ip = toString(addr) + "/" + to_string(prefix); + std::string ip = toString(addr) + "/" + std::to_string(prefix); mClient->callSync( api::ipc::METHOD_DELETE_NETDEV_IP_ADDRESS, std::make_shared( @@ -666,7 +665,7 @@ VsmStatus Client::vsm_netdev_del_ipv6_addr(const char* id, IS_SET(addr); //CIDR notation - string ip = toString(addr) + "/" + to_string(prefix); + std::string ip = toString(addr) + "/" + std::to_string(prefix); mClient->callSync( api::ipc::METHOD_DELETE_NETDEV_IP_ADDRESS, std::make_shared( @@ -684,8 +683,8 @@ VsmStatus Client::vsm_netdev_up(const char* id, const char* netdevId) noexcept mClient->callSync( api::ipc::METHOD_SET_NETDEV_ATTRS, std::make_shared( - api::SetNetDevAttrsIn{ id, netdevId, { { "flags", to_string(IFF_UP) }, - { "change", to_string(IFF_UP) } } })); + api::SetNetDevAttrsIn{ id, netdevId, { { "flags", std::to_string(IFF_UP) }, + { "change", std::to_string(IFF_UP) } } })); }); } @@ -698,8 +697,8 @@ VsmStatus Client::vsm_netdev_down(const char* id, const char* netdevId) noexcept mClient->callSync( api::ipc::METHOD_SET_NETDEV_ATTRS, std::make_shared( - api::SetNetDevAttrsIn{ id, netdevId, { { "flags", to_string(~IFF_UP) }, - { "change", to_string(IFF_UP) } } })); + api::SetNetDevAttrsIn{ id, netdevId, { { "flags", std::to_string(~IFF_UP) }, + { "change", std::to_string(IFF_UP) } } })); }); } diff --git a/client/vasum-client-impl.hpp b/client/vasum-client-impl.hpp index dbc9001..7278e68 100644 --- a/client/vasum-client-impl.hpp +++ b/client/vasum-client-impl.hpp @@ -81,7 +81,7 @@ typedef struct { * * Client uses dbus API. */ -class Client { +class Client final { public: Client() noexcept; ~Client() noexcept; diff --git a/client/vasum-client.cpp b/client/vasum-client.cpp index 9cf15b3..ccc21d5 100644 --- a/client/vasum-client.cpp +++ b/client/vasum-client.cpp @@ -33,8 +33,6 @@ #define API __attribute__((visibility("default"))) #endif // API -using namespace std; - namespace { Client& getClient(VsmClient client) @@ -77,7 +75,7 @@ API VsmStatus vsm_get_dispatcher_type(VsmClient client, VsmDispacherType* dispac API VsmClient vsm_client_create() { - Client* clientPtr = new(nothrow) Client(); + Client* clientPtr = new(std::nothrow) Client(); return reinterpret_cast(clientPtr); } diff --git a/server/netdev.cpp b/server/netdev.cpp index 8387c84..9565ed3 100644 --- a/server/netdev.cpp +++ b/server/netdev.cpp @@ -63,18 +63,18 @@ #define BRIDGE_FLAGS_MASTER 1 #endif -using namespace std; using namespace utils; using namespace vasum::netlink; +using std::get; namespace vasum { namespace netdev { namespace { -string getUniqueVethName() +std::string getUniqueVethName() { - auto find = [](const ifaddrs* ifaddr, const string& name) -> bool { + auto find = [](const ifaddrs* ifaddr, const std::string& name) -> bool { for (const ifaddrs* ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { if (name == ifa->ifa_name) { return true; @@ -85,17 +85,17 @@ string getUniqueVethName() ifaddrs* ifaddr; getifaddrs(&ifaddr); - string newName; + std::string newName; int i = 0; do { - newName = "veth0" + to_string(++i); + newName = "veth0" + std::to_string(++i); } while (find(ifaddr, newName)); freeifaddrs(ifaddr); return newName; } -uint32_t getInterfaceIndex(const string& name) { +uint32_t getInterfaceIndex(const std::string& name) { uint32_t index = if_nametoindex(name.c_str()); if (!index) { const std::string msg = "Can't get " + name + " interface index (" + getSystemErrorMessage() + ")"; @@ -105,7 +105,7 @@ uint32_t getInterfaceIndex(const string& name) { return index; } -uint32_t getInterfaceIndex(const string& name, pid_t nsPid) { +uint32_t getInterfaceIndex(const std::string& name, pid_t nsPid) { NetlinkMessage nlm(RTM_GETLINK, NLM_F_REQUEST | NLM_F_ACK); ifinfomsg infoPeer = utils::make_clean(); infoPeer.ifi_family = AF_UNSPEC; @@ -126,14 +126,14 @@ int getIpFamily(const std::string& ip) return ip.find(':') == std::string::npos ? AF_INET : AF_INET6; } -void validateNetdevName(const string& name) +void validateNetdevName(const std::string& name) { if (name.size() <= 1 || name.size() >= IFNAMSIZ) { throw ZoneOperationException("Invalid netdev name format"); } } -void createPipedNetdev(const string& netdev1, const string& netdev2) +void createPipedNetdev(const std::string& netdev1, const std::string& netdev2) { validateNetdevName(netdev1); validateNetdevName(netdev2); @@ -156,7 +156,7 @@ void createPipedNetdev(const string& netdev1, const string& netdev2) send(nlm); } -void attachToBridge(const string& bridge, const string& netdev) +void attachToBridge(const std::string& bridge, const std::string& netdev) { validateNetdevName(bridge); validateNetdevName(netdev); @@ -184,7 +184,7 @@ void attachToBridge(const string& bridge, const string& netdev) close(fd); } -int setFlags(const string& name, uint32_t mask, uint32_t flags) +int setFlags(const std::string& name, uint32_t mask, uint32_t flags) { uint32_t index = getInterfaceIndex(name); NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK); @@ -199,12 +199,12 @@ int setFlags(const string& name, uint32_t mask, uint32_t flags) return 0; } -void up(const string& netdev) +void up(const std::string& netdev) { setFlags(netdev, IFF_UP, IFF_UP); } -void moveToNS(const string& netdev, pid_t pid) +void moveToNS(const std::string& netdev, pid_t pid) { uint32_t index = getInterfaceIndex(netdev); NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK); @@ -216,7 +216,7 @@ void moveToNS(const string& netdev, pid_t pid) send(nlm); } -void createMacvlan(const string& master, const string& slave, const macvlan_mode& mode) +void createMacvlan(const std::string& master, const std::string& slave, const macvlan_mode& mode) { validateNetdevName(master); validateNetdevName(slave); @@ -283,7 +283,7 @@ std::vector getIpAddresses(const pid_t nsPid, int family, uint32_t index) LOGE(msg); throw VasumException(msg); } - attrs.push_back(make_tuple("ip", buf)); + attrs.push_back(std::make_tuple("ip", buf)); break; default: response.skipAttribute(); @@ -372,19 +372,19 @@ void deleteIpAddress(const pid_t nsPid, } // namespace -void createVeth(const pid_t& nsPid, const string& nsDev, const string& hostDev) +void createVeth(const pid_t& nsPid, const std::string& nsDev, const std::string& hostDev) { - string hostVeth = getUniqueVethName(); + std::string hostVeth = getUniqueVethName(); LOGT("Creating veth: bridge: " << hostDev << ", port: " << hostVeth << ", zone: " << nsDev); createPipedNetdev(nsDev, hostVeth); try { attachToBridge(hostDev, hostVeth); up(hostVeth); moveToNS(nsDev, nsPid); - } catch(const exception& ex) { + } catch(const std::exception& ex) { try { destroyNetdev(hostVeth); - } catch (const exception& ex) { + } catch (const std::exception& ex) { LOGE("Can't destroy netdev pipe: " << hostVeth << ", " << nsDev); } throw; @@ -392,8 +392,8 @@ void createVeth(const pid_t& nsPid, const string& nsDev, const string& hostDev) } void createMacvlan(const pid_t& nsPid, - const string& nsDev, - const string& hostDev, + const std::string& nsDev, + const std::string& hostDev, const macvlan_mode& mode) { LOGT("Creating macvlan: host: " << hostDev << ", zone: " << nsDev << ", mode: " << mode); @@ -401,17 +401,17 @@ void createMacvlan(const pid_t& nsPid, try { up(nsDev); moveToNS(nsDev, nsPid); - } catch(const exception& ex) { + } catch(const std::exception& ex) { try { destroyNetdev(nsDev); - } catch (const exception& ex) { + } catch (const std::exception& ex) { LOGE("Can't destroy netdev: " << nsDev); } throw; } } -void movePhys(const pid_t& nsPid, const string& devId) +void movePhys(const pid_t& nsPid, const std::string& devId) { LOGT("Creating phys: dev: " << devId); moveToNS(devId, nsPid); @@ -437,7 +437,7 @@ std::vector listNetdev(const pid_t& nsPid) return interfaces; } -void destroyNetdev(const string& netdev, const pid_t pid) +void destroyNetdev(const std::string& netdev, const pid_t pid) { LOGT("Destroying netdev: " << netdev); validateNetdevName(netdev); @@ -451,7 +451,7 @@ void destroyNetdev(const string& netdev, const pid_t pid) send(nlm, pid); } -void createBridge(const string& netdev) +void createBridge(const std::string& netdev) { LOGT("Creating bridge: " << netdev); validateNetdevName(netdev); @@ -477,7 +477,7 @@ Attrs getAttrs(const pid_t nsPid, const std::string& netdev) { auto joinAddresses = [](const Attrs& attrs) -> std::string { bool first = true; - stringstream ss; + std::stringstream ss; for (const auto& attr : attrs) { ss << (first ? "" : ",") << get<0>(attr) << ":" << get<1>(attr); first = false; @@ -539,7 +539,7 @@ Attrs getAttrs(const pid_t nsPid, const std::string& netdev) void setAttrs(const pid_t nsPid, const std::string& netdev, const Attrs& attrs) { - const set supportedAttrs{"flags", "change", "type", "mtu", "link", "ipv4", "ipv6"}; + const std::set supportedAttrs{"flags", "change", "type", "mtu", "link", "ipv4", "ipv6"}; LOGT("Setting network device informations: " << netdev); validateNetdevName(netdev); @@ -581,8 +581,8 @@ void setAttrs(const pid_t nsPid, const std::string& netdev, const Attrs& attrs) } //TODO: Multiple addresses should be set at once (add support NLM_F_MULTI to NetlinkMessage). - vector ipv4; - vector ipv6; + std::vector ipv4; + std::vector ipv6; for (const auto& attr : attrs) { if (get<0>(attr) == "ipv4") { ipv4.push_back(get<1>(attr)); @@ -592,14 +592,14 @@ void setAttrs(const pid_t nsPid, const std::string& netdev, const Attrs& attrs) } } - auto setIp = [nsPid](const vector& ips, uint32_t index, int family) -> void { + auto setIp = [nsPid](const std::vector& ips, uint32_t index, int family) -> void { using namespace boost::algorithm; for (const auto& ip : ips) { Attrs attrs; - vector params; + std::vector params; for (const auto& addrAttr : split(params, ip, is_any_of(","))) { size_t pos = addrAttr.find(":"); - if (pos == string::npos || pos == addrAttr.length()) { + if (pos == std::string::npos || pos == addrAttr.length()) { const std::string msg = "Wrong input data format: ill formed address attribute: " + addrAttr; LOGE(msg); throw VasumException(msg); @@ -620,7 +620,7 @@ void deleteIpAddress(const pid_t nsPid, { uint32_t index = getInterfaceIndex(netdev, nsPid); size_t slash = ip.find('/'); - if (slash == string::npos) { + if (slash == std::string::npos) { const std::string msg = "Wrong address format: it is not CIDR notation: can't find '/'"; LOGE(msg); throw VasumException(msg); @@ -639,4 +639,3 @@ void deleteIpAddress(const pid_t nsPid, } //namespace netdev } //namespace vasum - -- 2.7.4 From 732fd0b180e78e53b791c18dc39a18f1fa8e8e30 Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Mon, 12 Oct 2015 17:45:21 +0200 Subject: [PATCH 13/16] lxcpp: Adding init process to UTS, PID, NET, IPC namespaces [Feature] Starting init in some namespaces. MNT and USER need more work. [Cause] N/A [Solution] Refactored Guard [Verification] Build, install and run tests Change-Id: I3c5ec5c833da34c9ece6acf2c4965e8c57ab966d --- libs/lxcpp/container-config.hpp | 13 +++++- libs/lxcpp/container-impl.cpp | 6 ++- libs/lxcpp/container-impl.hpp | 1 + libs/lxcpp/container.hpp | 2 + libs/lxcpp/guard/guard.cpp | 73 ++++++++++++++++++--------------- libs/lxcpp/guard/guard.hpp | 11 ++++- libs/lxcpp/guard/main.cpp | 11 +++-- tests/unit_tests/lxcpp/ut-container.cpp | 1 + 8 files changed, 78 insertions(+), 40 deletions(-) diff --git a/libs/lxcpp/container-config.hpp b/libs/lxcpp/container-config.hpp index c84b4ef..9990bdc 100644 --- a/libs/lxcpp/container-config.hpp +++ b/libs/lxcpp/container-config.hpp @@ -109,7 +109,15 @@ struct ContainerConfig { */ TerminalsConfig mTerminals; - ContainerConfig() : mGuardPid(-1), mInitPid(-1) {} + /** + * Namespace types used to create the container + * + * Set: setNamespaces() + * Get: none + */ + int mNamespaces; + + ContainerConfig() : mGuardPid(-1), mInitPid(-1), mNamespaces(0) {} CONFIG_REGISTER ( @@ -119,7 +127,8 @@ struct ContainerConfig { mInitPid, mInit, mLogger, - mTerminals + mTerminals, + mNamespaces ) }; diff --git a/libs/lxcpp/container-impl.cpp b/libs/lxcpp/container-impl.cpp index d3258aa..5f0da35 100644 --- a/libs/lxcpp/container-impl.cpp +++ b/libs/lxcpp/container-impl.cpp @@ -25,7 +25,6 @@ #include "lxcpp/exception.hpp" #include "lxcpp/process.hpp" #include "lxcpp/filesystem.hpp" -#include "lxcpp/namespace.hpp" #include "lxcpp/capability.hpp" #include "lxcpp/commands/attach.hpp" #include "lxcpp/commands/start.hpp" @@ -169,6 +168,11 @@ void ContainerImpl::setTerminalCount(const unsigned int count) mConfig.mTerminals.count = count; } +void ContainerImpl::setNamespaces(const int namespaces) +{ + mConfig.mNamespaces = namespaces; +} + void ContainerImpl::start() { // TODO: check config consistency and completeness somehow diff --git a/libs/lxcpp/container-impl.hpp b/libs/lxcpp/container-impl.hpp index 041303b..846536e 100644 --- a/libs/lxcpp/container-impl.hpp +++ b/libs/lxcpp/container-impl.hpp @@ -56,6 +56,7 @@ public: void setTerminalCount(const unsigned int count); const std::vector& getNamespaces() const; + void setNamespaces(const int namespaces); // Execution actions void start(); diff --git a/libs/lxcpp/container.hpp b/libs/lxcpp/container.hpp index e3d537f..ba32e64 100644 --- a/libs/lxcpp/container.hpp +++ b/libs/lxcpp/container.hpp @@ -26,6 +26,7 @@ #include "lxcpp/network-config.hpp" #include "lxcpp/logger-config.hpp" + #include #include @@ -62,6 +63,7 @@ public: const std::string &arg = "") = 0; virtual void setTerminalCount(const unsigned int count) = 0; + virtual void setNamespaces(const int namespaces) = 0; // Execution actions virtual void start() = 0; diff --git a/libs/lxcpp/guard/guard.cpp b/libs/lxcpp/guard/guard.cpp index 28e572a..4f56771 100644 --- a/libs/lxcpp/guard/guard.cpp +++ b/libs/lxcpp/guard/guard.cpp @@ -32,64 +32,71 @@ #include #include - namespace lxcpp { +namespace { -void startContainer(const ContainerConfig &cfg) +int startContainer(void* data) { - lxcpp::execve(cfg.mInit); + ContainerConfig& config = *static_cast(data); + + // TODO: container preparation part 2 + + PrepGuestTerminal terminals(config.mTerminals); + terminals.execute(); + + lxcpp::execve(config.mInit); + + return EXIT_FAILURE; } -int startGuard(int channelFD) +} // namespace + + +Guard::Guard(const int channelFD) + : mChannel(channelFD) { - ContainerConfig cfg; - utils::Channel channel(channelFD); - channel.setCloseOnExec(true); - config::loadFromFD(channel.getFD(), cfg); + mChannel.setCloseOnExec(true); + config::loadFromFD(mChannel.getFD(), mConfig); - logger::setupLogger(cfg.mLogger.getType(), - cfg.mLogger.getLevel(), - cfg.mLogger.getArg()); + logger::setupLogger(mConfig.mLogger.getType(), + mConfig.mLogger.getLevel(), + mConfig.mLogger.getArg()); - LOGD("Guard started, config & logging restored"); + LOGD("Config & logging restored"); try { LOGD("Setting the guard process title"); - const std::string title = "[LXCPP] " + cfg.mName + " " + cfg.mRootPath; + const std::string title = "[LXCPP] " + mConfig.mName + " " + mConfig.mRootPath; setProcTitle(title); } catch (std::exception &e) { // Ignore, this is optional LOGW("Failed to set the guard process title: " << e.what()); } +} - // TODO: container preparation part 1 - - // TODO: switch to clone - LOGD("Forking container's init process"); - pid_t pid = lxcpp::fork(); - - if (pid == 0) { - // TODO: container preparation part 2 +Guard::~Guard() +{ +} - PrepGuestTerminal terminals(cfg.mTerminals); - terminals.execute(); +int Guard::execute() +{ + // TODO: container preparation part 1 - startContainer(cfg); - ::_exit(EXIT_FAILURE); - } + const pid_t initPid = lxcpp::clone(startContainer, + &mConfig, + mConfig.mNamespaces); - cfg.mGuardPid = ::getpid(); - cfg.mInitPid = pid; + mConfig.mGuardPid = ::getpid(); + mConfig.mInitPid = initPid; - channel.write(cfg.mGuardPid); - channel.write(cfg.mInitPid); - channel.shutdown(); + mChannel.write(mConfig.mGuardPid); + mChannel.write(mConfig.mInitPid); + mChannel.shutdown(); - int status = lxcpp::waitpid(pid); + int status = lxcpp::waitpid(initPid); LOGD("Init exited with status: " << status); return status; } - } // namespace lxcpp diff --git a/libs/lxcpp/guard/guard.hpp b/libs/lxcpp/guard/guard.hpp index 78457ae..741d72a 100644 --- a/libs/lxcpp/guard/guard.hpp +++ b/libs/lxcpp/guard/guard.hpp @@ -28,11 +28,20 @@ #include "lxcpp/container-config.hpp" #include "utils/channel.hpp" + namespace lxcpp { +class Guard { +public: + Guard(const int channelFD); + ~Guard(); -int startGuard(int channelFD); + int execute(); +private: + utils::Channel mChannel; + ContainerConfig mConfig; +}; } // namespace lxcpp diff --git a/libs/lxcpp/guard/main.cpp b/libs/lxcpp/guard/main.cpp index 78c7476..ff26418 100644 --- a/libs/lxcpp/guard/main.cpp +++ b/libs/lxcpp/guard/main.cpp @@ -35,8 +35,6 @@ int main(int argc, char *argv[]) ::_exit(EXIT_FAILURE); } - int channelFD = std::stoi(argv[1]); - // NOTE: this might not be required now, but I leave it here not to forget. // We need to investigate this with vasum and think about possibility of // poorly written software that leaks file descriptors and might use LXCPP. @@ -48,5 +46,12 @@ int main(int argc, char *argv[]) } #endif - return lxcpp::startGuard(channelFD); + try { + int fd = std::stoi(argv[1]); + lxcpp::Guard guard(fd); + return guard.execute(); + } catch(std::exception& e) { + // LOGE("Unexpected: " << utils::getTypeName(e) << ": " << e.what()); + return EXIT_FAILURE; + } } diff --git a/tests/unit_tests/lxcpp/ut-container.cpp b/tests/unit_tests/lxcpp/ut-container.cpp index 1c1a76c..17eee93 100644 --- a/tests/unit_tests/lxcpp/ut-container.cpp +++ b/tests/unit_tests/lxcpp/ut-container.cpp @@ -116,6 +116,7 @@ BOOST_AUTO_TEST_CASE(StartStop) BOOST_CHECK_NO_THROW(c->setLogger(logger::LogType::LOG_PERSISTENT_FILE, logger::LogLevel::DEBUG, LOGGER_FILE)); + BOOST_CHECK_NO_THROW(c->setNamespaces(CLONE_NEWIPC | CLONE_NEWNET | CLONE_NEWPID | CLONE_NEWUTS)); BOOST_CHECK_NO_THROW(c->start()); BOOST_CHECK_NO_THROW(c->stop()); } -- 2.7.4 From fdd084637d80bc804cced5e846158e8ac31c2252 Mon Sep 17 00:00:00 2001 From: Lukasz Pawelczyk Date: Thu, 8 Oct 2015 13:28:31 +0200 Subject: [PATCH 14/16] Implement Container::console() [Feature] An ability to connect to a PTY given to the container [Cause] To be able to use the container [Solution] Use PTYs, connect to them and pass read/writes between stdin/stdout and pty master. [Verification] There is a simple test program in junk for now Additional changes with patchset: - extended signal functions - fixed signalFD class Change-Id: Ia6320ee32d537311ef2675eb79f3e837192251b8 --- common/utils/signal.cpp | 87 ++++++++- common/utils/signal.hpp | 9 +- common/utils/signalfd.cpp | 58 +++--- common/utils/signalfd.hpp | 14 +- junk/Makefile | 19 ++ junk/run-shell.cpp | 81 ++++++++ libs/ipc/epoll/event-poll.cpp | 2 +- libs/lxcpp/CMakeLists.txt | 4 +- libs/lxcpp/commands/console.cpp | 292 ++++++++++++++++++++++++++++ libs/lxcpp/commands/console.hpp | 97 +++++++++ libs/lxcpp/commands/prep-guest-terminal.cpp | 1 - libs/lxcpp/commands/prep-host-terminal.cpp | 4 +- libs/lxcpp/commands/prep-host-terminal.hpp | 4 +- libs/lxcpp/container-impl.cpp | 33 +--- libs/lxcpp/container-impl.hpp | 1 + libs/lxcpp/container.hpp | 1 + libs/lxcpp/terminal-config.hpp | 4 +- libs/lxcpp/terminal.cpp | 36 ++-- libs/lxcpp/terminal.hpp | 15 +- server/server.cpp | 4 +- tests/unit_tests/utils/ut-signalfd.cpp | 4 +- 21 files changed, 668 insertions(+), 102 deletions(-) create mode 100644 junk/Makefile create mode 100644 junk/run-shell.cpp create mode 100644 libs/lxcpp/commands/console.cpp create mode 100644 libs/lxcpp/commands/console.hpp diff --git a/common/utils/signal.cpp b/common/utils/signal.cpp index 51c45b1..d961879 100644 --- a/common/utils/signal.cpp +++ b/common/utils/signal.cpp @@ -48,13 +48,13 @@ void setSignalMask(int how, const ::sigset_t& set) void changeSignal(int how, const int sigNum) { ::sigset_t set; if(-1 == ::sigemptyset(&set)) { - const std::string msg = "Error in sigfillset: " + getSystemErrorMessage(); + const std::string msg = "Error in sigemptyset: " + getSystemErrorMessage(); LOGE(msg); throw UtilsException(msg); } if(-1 ==::sigaddset(&set, sigNum)) { - const std::string msg = "Error in sigdelset: " + getSystemErrorMessage(); + const std::string msg = "Error in sigaddset: " + getSystemErrorMessage(); LOGE(msg); throw UtilsException(msg); } @@ -76,6 +76,62 @@ void changeSignal(int how, const int sigNum) { return set; } +bool isSignalPending(const int sigNum) +{ + ::sigset_t set; + if (::sigpending(&set) == -1) { + const std::string msg = "Error in sigpending: " + getSystemErrorMessage(); + LOGE(msg); + throw UtilsException(msg); + } + + int ret = ::sigismember(&set, sigNum); + if (ret == -1) { + const std::string msg = "Error in sigismember: " + getSystemErrorMessage(); + LOGE(msg); + throw UtilsException(msg); + } + + return ret; +} + +bool waitForSignal(const int sigNum, int timeoutMs) +{ + int timeoutS = timeoutMs / 1000; + timeoutMs -= timeoutS * 1000; + + struct ::timespec timeout = { + timeoutS, + timeoutMs * 1000000 + }; + + ::sigset_t set; + if(-1 == ::sigemptyset(&set)) { + const std::string msg = "Error in sigemptyset: " + getSystemErrorMessage(); + LOGE(msg); + throw UtilsException(msg); + } + + if(-1 ==::sigaddset(&set, sigNum)) { + const std::string msg = "Error in sigaddset: " + getSystemErrorMessage(); + LOGE(msg); + throw UtilsException(msg); + } + + ::siginfo_t info; + if (::sigtimedwait(&set, &info, &timeout) == -1) { + if (errno == EAGAIN) { + return false; + } + + const std::string msg = "Error in sigtimedwait: " + getSystemErrorMessage(); + LOGE(msg); + throw UtilsException(msg); + } + + return true; +} + bool isSignalBlocked(const int sigNum) { ::sigset_t set = getSignalMask(); @@ -119,18 +175,36 @@ void signalUnblock(const int sigNum) changeSignal(SIG_UNBLOCK, sigNum); } -void signalIgnore(const std::initializer_list& signals) +std::vector> signalIgnore(const std::initializer_list& signals) { struct ::sigaction act; + struct ::sigaction old; act.sa_handler = SIG_IGN; + std::vector> oldAct; for(const int s: signals) { - if(-1 == ::sigaction(s, &act, nullptr)) { + if(-1 == ::sigaction(s, &act, &old)) { const std::string msg = "Error in sigaction: " + getSystemErrorMessage(); LOGE(msg); throw UtilsException(msg); } + oldAct.emplace_back(s, old); } + + return oldAct; +} + +struct ::sigaction signalSet(const int sigNum, const struct ::sigaction *sigAct) +{ + struct ::sigaction old; + + if(-1 == ::sigaction(sigNum, sigAct, &old)) { + const std::string msg = "Error in sigaction: " + getSystemErrorMessage(); + LOGE(msg); + throw UtilsException(msg); + } + + return old; } void sendSignal(const pid_t pid, const int sigNum) @@ -145,8 +219,3 @@ void sendSignal(const pid_t pid, const int sigNum) } } // namespace utils - - - - - diff --git a/common/utils/signal.hpp b/common/utils/signal.hpp index f4a42e3..cb64419 100644 --- a/common/utils/signal.hpp +++ b/common/utils/signal.hpp @@ -27,17 +27,24 @@ #include #include +#include + namespace utils { + ::sigset_t getSignalMask(); +bool isSignalPending(const int sigNum); +bool waitForSignal(const int sigNum, int timeoutMs); bool isSignalBlocked(const int sigNum); void signalBlockAllExcept(const std::initializer_list& signals); void signalBlock(const int sigNum); void signalUnblock(const int sigNum); -void signalIgnore(const std::initializer_list& signals); +std::vector> signalIgnore(const std::initializer_list& signals); +struct ::sigaction signalSet(const int sigNum, const struct ::sigaction *sigAct); void sendSignal(const pid_t pid, const int sigNum); + } // namespace utils diff --git a/common/utils/signalfd.cpp b/common/utils/signalfd.cpp index 61d04d5..0156532 100644 --- a/common/utils/signalfd.cpp +++ b/common/utils/signalfd.cpp @@ -35,9 +35,14 @@ namespace utils { SignalFD::SignalFD(ipc::epoll::EventPoll& eventPoll) :mEventPoll(eventPoll) { - ::sigset_t set = getSignalMask(); + int error = ::sigemptyset(&mSet); + if (error == -1) { + const std::string msg = "Error in sigemptyset: " + getSystemErrorMessage(); + LOGE(msg); + throw UtilsException(msg); + } - mFD = ::signalfd(-1, &set, SFD_CLOEXEC); + mFD = ::signalfd(-1, &mSet, SFD_CLOEXEC); if (mFD == -1) { const std::string msg = "Error in signalfd: " + getSystemErrorMessage(); LOGE(msg); @@ -51,6 +56,20 @@ SignalFD::~SignalFD() { mEventPoll.removeFD(mFD); utils::close(mFD); + + // Unblock the signals that have been blocked previously, but also eat + // them if they were pending. It seems that signals are delivered twice, + // independently for signalfd and async. If we don't eat them before + // unblocking they will be delivered immediately potentially doing harm. + for (const int sigNum : mBlockedSignals) { + waitForSignal(sigNum, 0); + + // Yes, there is a race here between waitForSignal and signalUnlock, but if + // a signal is sent at this point it's not by us, signalFD is inactive. So + // if that is the case I'd expect someone to have set some handler already. + + signalUnblock(sigNum); + } } int SignalFD::getFD() const @@ -62,35 +81,30 @@ void SignalFD::setHandler(const int sigNum, const Callback&& callback) { Lock lock(mMutex); - ::sigset_t set = getSignalMask(); - - int error = ::signalfd(mFD, &set, SFD_CLOEXEC); - if (error != mFD) { - const std::string msg = "Error in signalfd: " + getSystemErrorMessage(); - LOGE(msg); - throw UtilsException(msg); - } - - mCallbacks.insert({sigNum, callback}); -} - -void SignalFD::setHandlerAndBlock(const int sigNum, const Callback&& callback) -{ - Lock lock(mMutex); - bool isBlocked = isSignalBlocked(sigNum); - - ::sigset_t set = getSignalMask(); if(!isBlocked) { signalBlock(sigNum); + mBlockedSignals.push_back(sigNum); + } + + int error = ::sigaddset(&mSet, sigNum); + if (error == -1) { + const std::string msg = getSystemErrorMessage(); + LOGE("Error in signalfd: " << msg); + if(!isBlocked) { + signalUnblock(sigNum); + mBlockedSignals.pop_back(); + } + throw UtilsException("Error in signalfd: " + msg); } - int error = ::signalfd(mFD, &set, SFD_CLOEXEC); + error = ::signalfd(mFD, &mSet, SFD_CLOEXEC); if (error != mFD) { const std::string msg = getSystemErrorMessage(); LOGE("Error in signalfd: " << msg); if(!isBlocked) { signalUnblock(sigNum); + mBlockedSignals.pop_back(); } throw UtilsException("Error in signalfd: " + msg); } @@ -103,7 +117,7 @@ void SignalFD::handleInternal() signalfd_siginfo sigInfo; utils::read(mFD, &sigInfo, sizeof(sigInfo)); - LOGD("Got signal: " << sigInfo.ssi_signo); + LOGT("Got signal: " << sigInfo.ssi_signo); { Lock lock(mMutex); diff --git a/common/utils/signalfd.hpp b/common/utils/signalfd.hpp index 4efed4b..cc72ed9 100644 --- a/common/utils/signalfd.hpp +++ b/common/utils/signalfd.hpp @@ -34,6 +34,7 @@ #include #include #include +#include namespace utils { @@ -55,7 +56,7 @@ public: /** * Add a callback for a specified signal. - * Doesn't block the signal. + * Blocks the async signal handler if it's not already blocked. * * @param sigNum number of the signal * @param callback handler callback @@ -63,15 +64,6 @@ public: void setHandler(const int sigNum, const Callback&& callback); /** - * Add a callback for a specified signal - * Blocks the asynchronous signal handling. - * - * @param sigNum number of the signal - * @param callback handler callback - */ - void setHandlerAndBlock(const int sigNum, const Callback&& callback); - - /** * @return signal file descriptor */ int getFD() const; @@ -80,9 +72,11 @@ private: typedef std::unique_lock Lock; int mFD; + ::sigset_t mSet; std::mutex mMutex; ipc::epoll::EventPoll& mEventPoll; std::unordered_map mCallbacks; + std::vector mBlockedSignals; void handleInternal(); }; diff --git a/junk/Makefile b/junk/Makefile new file mode 100644 index 0000000..4b93782 --- /dev/null +++ b/junk/Makefile @@ -0,0 +1,19 @@ +C=clang +CPP=clang++ +NAME1=run-shell +CPPFLAGS=$(shell PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig/ pkg-config --cflags liblxcpp libLogger) -g -std=c++11 +LDFLAGS=$(shell PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig/ pkg-config --libs liblxcpp libLogger) -g + +OBJECTS1=$(NAME1).o +HEADERS= + +all: $(NAME1) + +%.o: %.cpp $(HEADERS) + $(CPP) -c -o $@ $< $(CPPFLAGS) + +$(NAME1): $(OBJECTS1) + $(CPP) $(OBJECTS1) $(LDFLAGS) -o $(NAME1) + +clean: + rm -f $(OBJECTS1) $(NAME1) diff --git a/junk/run-shell.cpp b/junk/run-shell.cpp new file mode 100644 index 0000000..81ee0d8 --- /dev/null +++ b/junk/run-shell.cpp @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace lxcpp; + +pid_t initPid = -1; + +void sighandler(int signal) +{ + // remove the log in a deffered manner + pid_t pid = fork(); + + if (pid == 0) + { + pid_t pid = fork(); + + if (pid == 0) + { + if (initPid > 0) + ::kill(initPid, SIGTERM); + sleep(11); + ::unlink("/tmp/lxcpp-shell.txt"); + + exit(0); + } + + exit(0); + } + + wait(); + exit(0); +} + +int main(int argc, char *argv[]) +{ + signal(SIGINT, &sighandler); + + logger::setupLogger(logger::LogType::LOG_STDERR, logger::LogLevel::TRACE); + LOGT("Color test: TRACE"); + LOGD("Color test: DEBUG"); + LOGI("Color test: INFO"); + LOGW("Color test: WARN"); + LOGE("Color test: ERROR"); + + logger::setupLogger(logger::LogType::LOG_STDERR, logger::LogLevel::DEBUG); + //logger::setupLogger(logger::LogType::LOG_FILE, logger::LogLevel::TRACE, "/tmp/lxcpp-shell.txt"); + + std::vector args; + args.push_back("/bin/bash"); + + try + { + Container* c = createContainer("test", "/"); + c->setInit(args); + c->setLogger(logger::LogType::LOG_FILE, logger::LogLevel::TRACE, "/tmp/lxcpp-shell.txt"); + c->setTerminalCount(4); + c->start(); + c->console(); + // You could run the console for the second time to see if it can be reattached + //c->console(); + + initPid = c->getInitPid(); + + delete c; + } + catch (const std::exception &e) + { + std::cout << "EXCEPTION: " << e.what() << std::endl; + } + + sighandler(3); + + return 0; +} diff --git a/libs/ipc/epoll/event-poll.cpp b/libs/ipc/epoll/event-poll.cpp index c4b1f40..8fe41b2 100644 --- a/libs/ipc/epoll/event-poll.cpp +++ b/libs/ipc/epoll/event-poll.cpp @@ -87,7 +87,7 @@ void EventPoll::addFD(const int fd, const Events events, Callback&& callback) void EventPoll::modifyFD(const int fd, const Events events) { - // No need to lock and check mCallbacks map + // No need to lock as modify doesn't touch the mCallbacks map if (!modifyFDInternal(fd, events)) { const std::string msg = "Could not modify fd: " + std::to_string(fd); LOGE(msg); diff --git a/libs/lxcpp/CMakeLists.txt b/libs/lxcpp/CMakeLists.txt index 0d35bad..e3056dc 100644 --- a/libs/lxcpp/CMakeLists.txt +++ b/libs/lxcpp/CMakeLists.txt @@ -51,7 +51,7 @@ TARGET_COMPILE_DEFINITIONS(${PROJECT_NAME} PRIVATE ATTACH_PATH="${LIBEXEC_DIR}/${ATTACH_CODENAME}" ) -ADD_DEPENDENCIES(${PROJECT_NAME} Common Logger) +ADD_DEPENDENCIES(${PROJECT_NAME} Common Logger Config Ipc) ## Link libraries ############################################################## FIND_PACKAGE(Boost COMPONENTS system filesystem) @@ -59,7 +59,7 @@ PKG_CHECK_MODULES(LXCPP_DEPS REQUIRED glib-2.0) INCLUDE_DIRECTORIES(${LIBS_FOLDER} ${COMMON_FOLDER}) INCLUDE_DIRECTORIES(SYSTEM ${Boost_INCLUDE_DIRS} ${LXCPP_DEPS_INCLUDE_DIRS} ${JSON_C_INCLUDE_DIRS}) -TARGET_LINK_LIBRARIES(${PROJECT_NAME} Common ${pkgs_LDFLAGS} ${Boost_LIBRARIES} Logger Config util) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} Common ${pkgs_LDFLAGS} util ${Boost_LIBRARIES} Logger Config Ipc) ## Generate the pc file ######################################################## CONFIGURE_FILE(${PC_FILE}.in ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE} @ONLY) diff --git a/libs/lxcpp/commands/console.cpp b/libs/lxcpp/commands/console.cpp new file mode 100644 index 0000000..3210945 --- /dev/null +++ b/libs/lxcpp/commands/console.cpp @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Lukasz Pawelczyk (l.pawelczyk@samsung.com) + * @brief Implementation of the console + */ + +#include "lxcpp/commands/console.hpp" +#include "lxcpp/exception.hpp" +#include "lxcpp/terminal.hpp" +#include "lxcpp/credentials.hpp" + +#include "ipc/epoll/event-poll.hpp" +#include "logger/logger.hpp" +#include "utils/fd-utils.hpp" +#include "utils/signal.hpp" + +#include +#include +#include +#include +#include + + +namespace lxcpp { + + +Console::Console(TerminalsConfig &terminals, unsigned int terminalNum) + : mTerminals(terminals), + mTerminalNum(terminalNum), + mServiceMode(false), + mQuitReason(ConsoleQuitReason::NONE), + mEventPoll(), + mSignalFD(mEventPoll), + appToTermOffset(0), + termToAppOffset(0) +{ + if (terminalNum >= terminals.count) { + const std::string msg = "Requested terminal number does not exist"; + LOGE(msg); + throw TerminalException(msg); + } +} + +Console::~Console() +{ +} + +void Console::execute() +{ + if (!lxcpp::isatty(STDIN_FILENO) || !lxcpp::isatty(STDOUT_FILENO)) { + const std::string msg = "Standard input is not a terminal, cannot launch the console"; + LOGE(msg); + throw TerminalException(msg); + } + + LOGD("Launching the console with: " << mTerminals.count << " pseudoterminal(s) on the guest side."); + std::cout << "Connected to the zone, escape characted is ^] or ^a q." << std::endl; + std::cout << "If the container has just a shell remember to set TERM to be equal to the one of your own terminal." << std::endl; + std::cout << "Terminal number: " << mTerminalNum << ", use ^a n/p to switch between them." << std::endl; + + setupTTY(); + resizePTY(); + + using namespace std::placeholders; + mEventPoll.addFD(STDIN_FILENO, EPOLLIN, std::bind(&Console::onStdInput, this, _1, _2)); + mEventPoll.addFD(STDOUT_FILENO, 0, std::bind(&Console::onStdOutput, this, _1, _2)); + mEventPoll.addFD(getCurrentFD(), EPOLLIN, std::bind(&Console::onPTY, this, _1, _2)); + + while (mQuitReason == ConsoleQuitReason::NONE) { + mEventPoll.dispatchIteration(-1); + } + + mEventPoll.removeFD(getCurrentFD()); + mEventPoll.removeFD(STDIN_FILENO); + mEventPoll.removeFD(STDOUT_FILENO); + + restoreTTY(); + + switch (mQuitReason) { + case ConsoleQuitReason::USER: + std::cout << std::endl << "User requested quit" << std::endl; + break; + case ConsoleQuitReason::ERR: + std::cout << std::endl << "There has been an error on the terminal, quitting" << std::endl; + break; + case ConsoleQuitReason::HUP: + std::cout << std::endl << "Terminal disconnected, quitting" << std::endl; + break; + default: + std::cout << std::endl << "Unknown error, quitting" << std::endl; + break; + } + + // make the class reusable with subsequent execute()s + mQuitReason = ConsoleQuitReason::NONE; + mServiceMode = false; +} + +void Console::setupTTY() +{ + // save signal state, ignore several signal, setup resize window signal + mSignalStates = utils::signalIgnore({SIGQUIT, SIGTERM, SIGINT, SIGHUP, SIGPIPE, SIGWINCH}); + mSignalFD.setHandler(SIGWINCH, std::bind(&Console::resizePTY, this)); + + // save terminal state + lxcpp::tcgetattr(STDIN_FILENO, &mTTYState); + + // set the current terminal in raw mode: + struct termios newTTY = mTTYState; + ::cfmakeraw(&newTTY); + lxcpp::tcsetattr(STDIN_FILENO, TCSAFLUSH, &newTTY); +} + +void Console::resizePTY() +{ + // resize the underlying PTY terminal to the size of the user terminal + struct winsize wsz; + utils::ioctl(STDIN_FILENO, TIOCGWINSZ, &wsz); + utils::ioctl(getCurrentFD(), TIOCSWINSZ, &wsz); +} + +void Console::restoreTTY() +{ + // restore signal state + for (const auto sigInfo : mSignalStates) { + utils::signalSet(sigInfo.first, &sigInfo.second); + } + + // restore terminal state + lxcpp::tcsetattr(STDIN_FILENO, TCSAFLUSH, &mTTYState); +} + +void Console::onPTY(int fd, ipc::epoll::Events events) +{ + if ((events & EPOLLIN) == EPOLLIN) { + const size_t avail = IO_BUFFER_SIZE - appToTermOffset; + char *buf = appToTerm + appToTermOffset; + + const ssize_t read = ::read(fd, buf, avail); + appToTermOffset += read; + + if (appToTermOffset) { + mEventPoll.modifyFD(STDOUT_FILENO, EPOLLOUT); + } + } + + if ((events & EPOLLOUT) == EPOLLOUT && termToAppOffset) { + const ssize_t written = ::write(fd, termToApp, termToAppOffset); + ::memmove(termToApp, termToApp + written, termToAppOffset - written); + termToAppOffset -= written; + + if (termToAppOffset == 0) { + mEventPoll.modifyFD(fd, EPOLLIN); + } + } + + checkForError(events); +} + +void Console::onStdInput(int fd, ipc::epoll::Events events) +{ + if ((events & EPOLLIN) == EPOLLIN) { + const size_t avail = IO_BUFFER_SIZE - termToAppOffset; + char *buf = termToApp + termToAppOffset; + const ssize_t read = ::read(fd, buf, avail); + + if (read == 1 && handleSpecial(buf[0])) { + return; + } + + termToAppOffset += read; + + if (termToAppOffset) { + mEventPoll.modifyFD(getCurrentFD(), EPOLLIN | EPOLLOUT); + } + } + + checkForError(events); +} + +void Console::onStdOutput(int fd, ipc::epoll::Events events) +{ + if ((events & EPOLLOUT) == EPOLLOUT && appToTermOffset) { + const ssize_t written = ::write(fd, appToTerm, appToTermOffset); + ::memmove(appToTerm, appToTerm + written, appToTermOffset - written); + appToTermOffset -= written; + + if (appToTermOffset == 0) { + mEventPoll.modifyFD(fd, 0); + } + } + + checkForError(events); +} + +void Console::checkForError(ipc::epoll::Events events) +{ + // TODO: ignore EPOLLHUP for now, this allows us to cycle through not + // connected terminals. When we can handle full containers with getty() + // processes we'll decide what to do about that. +#if 0 + if ((events & EPOLLHUP) == EPOLLHUP) { + mQuitReason = ConsoleQuitReason::HUP; + } +#endif + if ((events & EPOLLERR) == EPOLLERR) { + mQuitReason = ConsoleQuitReason::ERR; + } +} + +bool Console::handleSpecial(const char key) +{ + if (mServiceMode) { + mServiceMode = false; + + switch(key) { + case 'q': + mQuitReason = ConsoleQuitReason::USER; + return true; + case 'n': + consoleChange(ConsoleChange::NEXT); + return true; + case 'p': + consoleChange(ConsoleChange::PREV); + return true; + default: + return true; + } + } + + if (key == 0x1d) { // ^] + mQuitReason = ConsoleQuitReason::USER; + return true; + } + if (key == 0x01) { // ^a + mServiceMode = true; + return true; + } + + return false; +} + +void Console::consoleChange(ConsoleChange direction) +{ + mEventPoll.removeFD(getCurrentFD()); + + if (direction == ConsoleChange::NEXT) { + ++mTerminalNum; + } else if (direction == ConsoleChange::PREV) { + --mTerminalNum; + } + + mTerminalNum = (mTerminalNum + mTerminals.count) % mTerminals.count; + + int mode = EPOLLIN; + if (termToAppOffset) { + mode &= EPOLLOUT; + } + + restoreTTY(); + std::cout << "Terminal number: " << mTerminalNum << std::endl; + setupTTY(); + resizePTY(); + + using namespace std::placeholders; + mEventPoll.addFD(getCurrentFD(), mode, std::bind(&Console::onPTY, this, _1, _2)); +} + +int Console::getCurrentFD() const +{ + return mTerminals.PTYs[mTerminalNum].masterFD.value; +} + + +} // namespace lxcpp diff --git a/libs/lxcpp/commands/console.hpp b/libs/lxcpp/commands/console.hpp new file mode 100644 index 0000000..93ac248 --- /dev/null +++ b/libs/lxcpp/commands/console.hpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Lukasz Pawelczyk (l.pawelczyk@samsung.com) + * @brief Headers for the console + */ + +#ifndef LXCPP_COMMANDS_CONSOLE_HPP +#define LXCPP_COMMANDS_CONSOLE_HPP + +#include "lxcpp/commands/command.hpp" +#include "lxcpp/terminal-config.hpp" +#include "lxcpp/terminal.hpp" + +#include "ipc/epoll/event-poll.hpp" +#include "utils/signalfd.hpp" + +#include +#include + + +namespace lxcpp { + + +class Console final: Command { +public: + /** + * Launches the console on the current terminal + * + * @param terminals container's terminals config + * @param terminalNum initial terminal to attach to + */ + Console(TerminalsConfig &terminals, unsigned int terminalNum = 0); + ~Console(); + + void execute(); + +private: + enum class ConsoleQuitReason : int { + NONE = 0, + USER = 1, + HUP = 2, + ERR = 3 + }; + enum class ConsoleChange : int { + NEXT = 0, + PREV = 1 + }; + static const int IO_BUFFER_SIZE = 1024; + + TerminalsConfig &mTerminals; + int mTerminalNum; + bool mServiceMode; + ConsoleQuitReason mQuitReason; + ipc::epoll::EventPoll mEventPoll; + utils::SignalFD mSignalFD; + std::vector> mSignalStates; + struct termios mTTYState; + + char appToTerm[IO_BUFFER_SIZE]; + int appToTermOffset; + char termToApp[IO_BUFFER_SIZE]; + int termToAppOffset; + + void setupTTY(); + void restoreTTY(); + void resizePTY(); + void onPTY(int fd, ipc::epoll::Events events); + void onStdInput(int fd, ipc::epoll::Events events); + void onStdOutput(int fd, ipc::epoll::Events events); + void checkForError(ipc::epoll::Events events); + bool handleSpecial(char key); + void consoleChange(ConsoleChange direction); + int getCurrentFD() const; +}; + + +} // namespace lxcpp + + +#endif // LXCPP_COMMANDS_CONSOLE_HPP diff --git a/libs/lxcpp/commands/prep-guest-terminal.cpp b/libs/lxcpp/commands/prep-guest-terminal.cpp index f496f86..5722fef 100644 --- a/libs/lxcpp/commands/prep-guest-terminal.cpp +++ b/libs/lxcpp/commands/prep-guest-terminal.cpp @@ -57,7 +57,6 @@ void PrepGuestTerminal::execute() int fd = utils::open(mTerminals.PTYs[0].ptsName, O_RDWR | O_CLOEXEC | O_NOCTTY); setupIOControlTTY(fd); - } diff --git a/libs/lxcpp/commands/prep-host-terminal.cpp b/libs/lxcpp/commands/prep-host-terminal.cpp index 1dc4f3d..0b57f89 100644 --- a/libs/lxcpp/commands/prep-host-terminal.cpp +++ b/libs/lxcpp/commands/prep-host-terminal.cpp @@ -43,8 +43,8 @@ void PrepHostTerminal::execute() { LOGD("Creating " << mTerminals.count << " pseudoterminal(s) on the host side:"); - for (int i = 0; i < mTerminals.count; ++i) { - const auto pty = lxcpp::openPty(true); + for (unsigned int i = 0; i < mTerminals.count; ++i) { + const auto pty = lxcpp::openPty(false); LOGD(pty.second << " has been created"); mTerminals.PTYs.emplace_back(pty.first, pty.second); } diff --git a/libs/lxcpp/commands/prep-host-terminal.hpp b/libs/lxcpp/commands/prep-host-terminal.hpp index f7153b2..9902a2d 100644 --- a/libs/lxcpp/commands/prep-host-terminal.hpp +++ b/libs/lxcpp/commands/prep-host-terminal.hpp @@ -39,9 +39,9 @@ public: * It creates a number of pseudoterminals and stores them * to be passed to the guard and prepared for init process. * - * @param config container's config + * @param terminals container's terminals config */ - PrepHostTerminal(TerminalsConfig &config); + PrepHostTerminal(TerminalsConfig &terminals); ~PrepHostTerminal(); void execute(); diff --git a/libs/lxcpp/container-impl.cpp b/libs/lxcpp/container-impl.cpp index 5f0da35..09b165f 100644 --- a/libs/lxcpp/container-impl.cpp +++ b/libs/lxcpp/container-impl.cpp @@ -27,6 +27,7 @@ #include "lxcpp/filesystem.hpp" #include "lxcpp/capability.hpp" #include "lxcpp/commands/attach.hpp" +#include "lxcpp/commands/console.hpp" #include "lxcpp/commands/start.hpp" #include "lxcpp/commands/stop.hpp" #include "lxcpp/commands/prep-host-terminal.hpp" @@ -46,29 +47,6 @@ #include -namespace { - -// TODO: UGLY: REMOVEME: -// It will be removed as soon as Container::console() will get implemented -// I need it for now to know I didn't brake anything. It will be eradicated. -void readTerminal(const lxcpp::TerminalConfig &term) -{ - char *buf = NULL; - size_t size = 0; - ssize_t ret; - - printf("%s output:\n", term.ptsName.c_str()); - usleep(10000); - - FILE *fp = fdopen(term.masterFD.value, "r"); - while((ret = getline(&buf, &size, fp)) != -1L) { - printf("%s", buf); - } - free(buf); -} - -} // namespace - namespace lxcpp { ContainerImpl::ContainerImpl(const std::string &name, const std::string &path) @@ -182,9 +160,6 @@ void ContainerImpl::start() Start start(mConfig); start.execute(); - - // TODO: UGLY: REMOVEME: read from 1st terminal - readTerminal(mConfig.mTerminals.PTYs[0]); } void ContainerImpl::stop() @@ -228,6 +203,12 @@ void ContainerImpl::attach(const std::vector& argv, attach.execute(); } +void ContainerImpl::console() +{ + Console console(mConfig.mTerminals); + console.execute(); +} + const std::vector& ContainerImpl::getNamespaces() const { return mNamespaces; diff --git a/libs/lxcpp/container-impl.hpp b/libs/lxcpp/container-impl.hpp index 846536e..4b560f1 100644 --- a/libs/lxcpp/container-impl.hpp +++ b/libs/lxcpp/container-impl.hpp @@ -68,6 +68,7 @@ public: // Other void attach(const std::vector& argv, const std::string& cwdInContainer); + void console(); // Network interfaces setup/config /** diff --git a/libs/lxcpp/container.hpp b/libs/lxcpp/container.hpp index ba32e64..2ca3a10 100644 --- a/libs/lxcpp/container.hpp +++ b/libs/lxcpp/container.hpp @@ -75,6 +75,7 @@ public: // Other virtual void attach(const std::vector& argv, const std::string& cwdInContainer) = 0; + virtual void console() = 0; // Network interfaces setup/config virtual void addInterfaceConfig(const std::string& hostif, diff --git a/libs/lxcpp/terminal-config.hpp b/libs/lxcpp/terminal-config.hpp index 3d6675a..f4a675c 100644 --- a/libs/lxcpp/terminal-config.hpp +++ b/libs/lxcpp/terminal-config.hpp @@ -55,10 +55,10 @@ struct TerminalConfig { }; struct TerminalsConfig { - int count; + unsigned int count; std::vector PTYs; - TerminalsConfig(const int count = 1) + TerminalsConfig(const unsigned int count = 1) : count(count) {} diff --git a/libs/lxcpp/terminal.cpp b/libs/lxcpp/terminal.cpp index 72c57af..5374ced 100644 --- a/libs/lxcpp/terminal.cpp +++ b/libs/lxcpp/terminal.cpp @@ -49,24 +49,6 @@ void openpty(int *master, int *slave) } } -void tcgetattr(const int fd, struct termios *termios_p) -{ - if (-1 == ::tcgetattr(fd, termios_p)) { - const std::string msg = "tcgetattr() failed: " + utils::getSystemErrorMessage(); - LOGE(msg); - throw TerminalException(msg); - } -} - -void tcsetattr(const int fd, const int optional_actions, const struct termios *termios_p) -{ - if (-1 == ::tcsetattr(fd, optional_actions, termios_p)) { - const std::string msg = "tcsetattr() failed: " + utils::getSystemErrorMessage(); - LOGE(msg); - throw TerminalException(msg); - } -} - std::string ttyname_r(const int fd) { char ptsName[PATH_MAX]; @@ -135,6 +117,24 @@ bool isatty(int fd) throw TerminalException(msg); } +void tcgetattr(const int fd, struct termios *termios_p) +{ + if (-1 == ::tcgetattr(fd, termios_p)) { + const std::string msg = "tcgetattr() failed: " + utils::getSystemErrorMessage(); + LOGE(msg); + throw TerminalException(msg); + } +} + +void tcsetattr(const int fd, const int optional_actions, const struct termios *termios_p) +{ + if (-1 == ::tcsetattr(fd, optional_actions, termios_p)) { + const std::string msg = "tcsetattr() failed: " + utils::getSystemErrorMessage(); + LOGE(msg); + throw TerminalException(msg); + } +} + void setupIOControlTTY(const int ttyFD) { if (!lxcpp::isatty(ttyFD)) { diff --git a/libs/lxcpp/terminal.hpp b/libs/lxcpp/terminal.hpp index 421e4ce..7b024f0 100644 --- a/libs/lxcpp/terminal.hpp +++ b/libs/lxcpp/terminal.hpp @@ -24,6 +24,8 @@ #ifndef LXCPP_TERMINAL_HPP #define LXCPP_TERMINAL_HPP +#include + namespace lxcpp { @@ -40,11 +42,21 @@ namespace lxcpp { int nullStdFDs(); /** - * Checks if a file descriptor is a terminal + * Checks if a file descriptor is a terminal. */ bool isatty(int fd); /** + * Get terminal attributes. + */ +void tcgetattr(const int fd, struct termios *termios_p); + +/** + * Set terminal attributes. + */ +void tcsetattr(const int fd, const int optional_actions, const struct termios *termios_p); + +/** * Setups the passed fd as a new control and IO (in, out, err) terminal */ void setupIOControlTTY(const int ttyFD); @@ -65,5 +77,4 @@ std::pair openPty(bool rawMode); } // namespace lxcpp - #endif // LXCPP_TERMINAL_HPP diff --git a/server/server.cpp b/server/server.cpp index 2fc8a65..bb028c7 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -84,8 +84,8 @@ Server::Server(const std::string& configPath) mZonesManager(mEventPoll, mConfigPath), mDispatchingThread(::pthread_self()) { - mSignalFD.setHandlerAndBlock(SIGUSR1, std::bind(&Server::handleUpdate, this)); - mSignalFD.setHandlerAndBlock(SIGINT, std::bind(&Server::handleStop, this)); + mSignalFD.setHandler(SIGUSR1, std::bind(&Server::handleUpdate, this)); + mSignalFD.setHandler(SIGINT, std::bind(&Server::handleStop, this)); mSignalFD.setHandler(SIGTERM, std::bind(&Server::handleStop, this)); } diff --git a/tests/unit_tests/utils/ut-signalfd.cpp b/tests/unit_tests/utils/ut-signalfd.cpp index 9f002e0..5a78b73 100644 --- a/tests/unit_tests/utils/ut-signalfd.cpp +++ b/tests/unit_tests/utils/ut-signalfd.cpp @@ -79,8 +79,8 @@ BOOST_AUTO_TEST_CASE(BlockingSignalHandler) { ipc::epoll::EventPoll poll; SignalFD s(poll); - s.setHandlerAndBlock(SIGUSR1, [](const int) {}); - s.setHandlerAndBlock(SIGINT, [](const int) {}); + s.setHandler(SIGUSR1, [](const int) {}); + s.setHandler(SIGINT, [](const int) {}); ::raise(SIGINT); std::this_thread::sleep_for(std::chrono::milliseconds(TIMEOUT)); -- 2.7.4 From edb255463ee3a03acd4a5171783d17e6ec82dbbc Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Tue, 13 Oct 2015 17:20:41 +0200 Subject: [PATCH 15/16] lxcpp: Removed Namespace enum [Feature] Regular Linux CLONE_* flags should be used instead of the removed enum [Cause] N/A [Solution] N/A [Verification] Build, install and run tests Change-Id: I29921d1387da76fb50b269f0e92ebb592cf51751 --- libs/lxcpp/attach/attach-config.hpp | 5 ++-- libs/lxcpp/container-config.hpp | 2 +- libs/lxcpp/container-impl.cpp | 11 ++++---- libs/lxcpp/container-impl.hpp | 5 +--- libs/lxcpp/container.hpp | 2 ++ libs/lxcpp/namespace.cpp | 41 +++++++---------------------- libs/lxcpp/namespace.hpp | 20 ++------------ libs/lxcpp/process.cpp | 46 +++++++++++++++++++-------------- libs/lxcpp/process.hpp | 10 ++----- tests/unit_tests/lxcpp/ut-namespace.cpp | 23 ++++++----------- tests/unit_tests/lxcpp/ut-process.cpp | 27 ++++++------------- 11 files changed, 67 insertions(+), 125 deletions(-) diff --git a/libs/lxcpp/attach/attach-config.hpp b/libs/lxcpp/attach/attach-config.hpp index 94e2626..beb425f 100644 --- a/libs/lxcpp/attach/attach-config.hpp +++ b/libs/lxcpp/attach/attach-config.hpp @@ -45,7 +45,7 @@ struct AttachConfig { pid_t initPid; /// Namespaces to which we'll attach - std::vector namespaces; + int namespaces; /// User ID to set uid_t uid; @@ -75,7 +75,7 @@ struct AttachConfig { AttachConfig(const std::vector& argv, const pid_t initPid, - const std::vector& namespaces, + const int namespaces, const uid_t uid, const gid_t gid, const std::vector& supplementaryGids, @@ -98,7 +98,6 @@ struct AttachConfig { CONFIG_REGISTER ( - //TODO: Uncomment and fix cstring serialization argv, initPid, namespaces, diff --git a/libs/lxcpp/container-config.hpp b/libs/lxcpp/container-config.hpp index 9990bdc..4fbf86d 100644 --- a/libs/lxcpp/container-config.hpp +++ b/libs/lxcpp/container-config.hpp @@ -113,7 +113,7 @@ struct ContainerConfig { * Namespace types used to create the container * * Set: setNamespaces() - * Get: none + * Get: getNamespaces() */ int mNamespaces; diff --git a/libs/lxcpp/container-impl.cpp b/libs/lxcpp/container-impl.cpp index 09b165f..84f9b44 100644 --- a/libs/lxcpp/container-impl.cpp +++ b/libs/lxcpp/container-impl.cpp @@ -151,6 +151,12 @@ void ContainerImpl::setNamespaces(const int namespaces) mConfig.mNamespaces = namespaces; } + +int ContainerImpl::getNamespaces() const +{ + return mConfig.mNamespaces; +} + void ContainerImpl::start() { // TODO: check config consistency and completeness somehow @@ -209,11 +215,6 @@ void ContainerImpl::console() console.execute(); } -const std::vector& ContainerImpl::getNamespaces() const -{ - return mNamespaces; -} - void ContainerImpl::addInterfaceConfig(const std::string& hostif, const std::string& zoneif, InterfaceType type, diff --git a/libs/lxcpp/container-impl.hpp b/libs/lxcpp/container-impl.hpp index 4b560f1..d3db492 100644 --- a/libs/lxcpp/container-impl.hpp +++ b/libs/lxcpp/container-impl.hpp @@ -55,8 +55,8 @@ public: void setTerminalCount(const unsigned int count); - const std::vector& getNamespaces() const; void setNamespaces(const int namespaces); + int getNamespaces() const; // Execution actions void start(); @@ -98,9 +98,6 @@ public: private: ContainerConfig mConfig; - - // TODO: convert to ContainerConfig struct - std::vector mNamespaces; }; } // namespace lxcpp diff --git a/libs/lxcpp/container.hpp b/libs/lxcpp/container.hpp index 2ca3a10..096463b 100644 --- a/libs/lxcpp/container.hpp +++ b/libs/lxcpp/container.hpp @@ -63,7 +63,9 @@ public: const std::string &arg = "") = 0; virtual void setTerminalCount(const unsigned int count) = 0; + virtual void setNamespaces(const int namespaces) = 0; + virtual int getNamespaces() const = 0; // Execution actions virtual void start() = 0; diff --git a/libs/lxcpp/namespace.cpp b/libs/lxcpp/namespace.cpp index 0ee8c88..57c8649 100644 --- a/libs/lxcpp/namespace.cpp +++ b/libs/lxcpp/namespace.cpp @@ -25,31 +25,22 @@ #include "lxcpp/exception.hpp" #include "logger/logger.hpp" -#include -#include - namespace lxcpp { -Namespace operator|(const Namespace a, const Namespace b) -{ - return static_cast(static_cast::type>(a) | - static_cast::type>(b)); -} - -std::string toString(const Namespace ns) +std::string nsToString(const int ns) { switch(ns) { - case Namespace::USER: + case CLONE_NEWUSER: return "user"; - case Namespace::MNT: + case CLONE_NEWNS: return "mnt"; - case Namespace::PID: + case CLONE_NEWPID: return "pid"; - case Namespace::UTS: + case CLONE_NEWUTS: return "uts"; - case Namespace::IPC: + case CLONE_NEWIPC: return "ipc"; - case Namespace::NET: + case CLONE_NEWNET: return "net"; default: const std::string msg = "Bad namespace passed to the function"; @@ -58,28 +49,14 @@ std::string toString(const Namespace ns) } } -int toFlag(const std::vector& namespaces) -{ - Namespace flag = std::accumulate(namespaces.begin(), - namespaces.end(), - static_cast(0), - std::bit_or()); - return static_cast(flag); -} - -int toFlag(const Namespace ns) -{ - return static_cast(ns); -} - std::string getNsPath(const pid_t pid) { return "/proc/" + std::to_string(pid) + "/ns"; } -std::string getPath(const pid_t pid, const Namespace ns) +std::string getPath(const pid_t pid, const int ns) { - return getNsPath(pid) + "/" + toString(ns); + return getNsPath(pid) + "/" + nsToString(ns); } } // namespace lxcpp diff --git a/libs/lxcpp/namespace.hpp b/libs/lxcpp/namespace.hpp index be6d033..91a4c9e 100644 --- a/libs/lxcpp/namespace.hpp +++ b/libs/lxcpp/namespace.hpp @@ -26,30 +26,14 @@ #include #include -#include namespace lxcpp { -enum class Namespace : int { - USER = CLONE_NEWUSER, - MNT = CLONE_NEWNS, - PID = CLONE_NEWPID, - UTS = CLONE_NEWUTS, - IPC = CLONE_NEWIPC, - NET = CLONE_NEWNET -}; - -Namespace operator |(const Namespace a, const Namespace b); - -std::string toString(const Namespace ns); +std::string nsToString(const int ns); std::string getNsPath(const pid_t pid); -std::string getPath(const pid_t pid, const Namespace ns); - -int toFlag(const Namespace ns); - -int toFlag(const std::vector& namespaces); +std::string getPath(const pid_t pid, const int ns); } // namespace lxcpp diff --git a/libs/lxcpp/process.cpp b/libs/lxcpp/process.cpp index 204dec2..6d2e6d8 100644 --- a/libs/lxcpp/process.cpp +++ b/libs/lxcpp/process.cpp @@ -34,6 +34,8 @@ #include #include +#include + namespace lxcpp { pid_t fork() @@ -69,40 +71,44 @@ pid_t clone(int (*function)(void *), return pid; } -pid_t clone(int (*function)(void *), - void *args, - const std::vector& namespaces, - const int additionalFlags) -{ - return clone(function, args, toFlag(namespaces) | additionalFlags); -} - -void setns(const pid_t pid, const std::vector& namespaces) +void setns(const pid_t pid, int requestedNamespaces) { int dirFD = utils::open(getNsPath(pid), O_DIRECTORY | O_CLOEXEC); + static const std::array NAMESPACES {{ + CLONE_NEWUSER, CLONE_NEWNS, CLONE_NEWPID, CLONE_NEWUTS, CLONE_NEWIPC, CLONE_NEWNET + }}; + // Open FDs connected with the requested namespaces - std::vector fds(namespaces.size(), -1); - for(size_t i = 0; i < namespaces.size(); ++i) { - fds[i] = ::openat(dirFD, - toString(namespaces[i]).c_str(), + std::vector fds; + for(const int ns: NAMESPACES) { + if (!(ns & requestedNamespaces)) { + // This namespace wasn't requested + continue; + } + + int fd = ::openat(dirFD, + nsToString(ns).c_str(), O_RDONLY | O_CLOEXEC); - if(fds[i] < 0) { + if(fd < 0) { const std::string msg = "openat() failed: " + utils::getSystemErrorMessage(); - for (size_t j = 0; j < i; ++j) { - utils::close(fds[j]); + // Cleanup file descriptors + for (const int d: fds) { + utils::close(d); } utils::close(dirFD); LOGE(msg); throw ProcessSetupException(msg); } + + fds.push_back(fd); } - // Setns for every namespace + // Setns to every requested namespace for(size_t i = 0; i < fds.size(); ++i) { - if(-1 == ::setns(fds[i], toFlag(namespaces[i]))) { + if(-1 == ::setns(fds[i], 0 /* we're sure it's a fd of the right namespace*/)) { const std::string msg = "setns() failed: " + utils::getSystemErrorMessage(); for (size_t j = i; j < fds.size(); ++j) { @@ -148,9 +154,9 @@ int waitpid(const pid_t pid) throw ProcessSetupException(msg); } -void unshare(const Namespace ns) +void unshare(const int ns) { - if(-1 == ::unshare(toFlag(ns))) { + if(-1 == ::unshare(ns)) { const std::string msg = "unshare() failed: " + utils::getSystemErrorMessage(); LOGE(msg); throw ProcessSetupException(msg); diff --git a/libs/lxcpp/process.hpp b/libs/lxcpp/process.hpp index 75c1e4f..2b33e4e 100644 --- a/libs/lxcpp/process.hpp +++ b/libs/lxcpp/process.hpp @@ -37,17 +37,11 @@ pid_t clone(int (*function)(void *), void *args, const int flags); -pid_t clone(int (*function)(void *), - void *args, - const std::vector& namespaces, - const int additionalFlags = 0); - -void setns(const pid_t pid, - const std::vector& namespaces); +void setns(const pid_t pid, const int namespaces); int waitpid(const pid_t pid); -void unshare(const Namespace ns); +void unshare(const int ns); void execve(const std::vector& argv); diff --git a/tests/unit_tests/lxcpp/ut-namespace.cpp b/tests/unit_tests/lxcpp/ut-namespace.cpp index 9e10e81..ba715c9 100644 --- a/tests/unit_tests/lxcpp/ut-namespace.cpp +++ b/tests/unit_tests/lxcpp/ut-namespace.cpp @@ -43,22 +43,15 @@ BOOST_FIXTURE_TEST_SUITE(LxcppNamespaceSuite, Fixture) using namespace lxcpp; -const std::array NAMESPACES {{ - Namespace::USER, - Namespace::MNT, - Namespace::PID, - Namespace::UTS, - Namespace::IPC, - Namespace::NET +static const std::array NAMESPACES {{ + CLONE_NEWUSER, + CLONE_NEWNS, + CLONE_NEWPID, + CLONE_NEWUTS, + CLONE_NEWIPC, + CLONE_NEWNET }}; -BOOST_AUTO_TEST_CASE(OR) -{ - Namespace a = Namespace::USER; - Namespace b = Namespace::MNT; - BOOST_CHECK_EQUAL(CLONE_NEWUSER | CLONE_NEWNS, static_cast(a | b)); -} - BOOST_AUTO_TEST_CASE(GetPath) { for(const auto ns: NAMESPACES) { @@ -69,7 +62,7 @@ BOOST_AUTO_TEST_CASE(GetPath) BOOST_AUTO_TEST_CASE(ToString) { for(const auto ns: NAMESPACES) { - toString(ns); + nsToString(ns); } } diff --git a/tests/unit_tests/lxcpp/ut-process.cpp b/tests/unit_tests/lxcpp/ut-process.cpp index 662dd82..6ec5ff0 100644 --- a/tests/unit_tests/lxcpp/ut-process.cpp +++ b/tests/unit_tests/lxcpp/ut-process.cpp @@ -42,7 +42,7 @@ struct Fixture { ~Fixture() {} }; -int clonefn(void* /*args*/) { +int clonefn(void*) { return 0; } @@ -52,19 +52,12 @@ BOOST_FIXTURE_TEST_SUITE(LxcppProcessSuite, Fixture) using namespace lxcpp; -const std::vector NAMESPACES {{ - Namespace::USER, - Namespace::MNT, - Namespace::PID, - Namespace::UTS, - Namespace::IPC, - Namespace::NET - }}; - BOOST_AUTO_TEST_CASE(Clone) { - BOOST_CHECK_NO_THROW(lxcpp::clone(clonefn, nullptr, NAMESPACES)); - BOOST_CHECK_NO_THROW(lxcpp::clone(clonefn, nullptr, {Namespace::MNT})); + BOOST_CHECK_NO_THROW(lxcpp::clone(clonefn, + nullptr, + CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWNET)); + BOOST_CHECK_NO_THROW(lxcpp::clone(clonefn, nullptr, CLONE_NEWNS)); } BOOST_AUTO_TEST_CASE(Setns) @@ -75,12 +68,8 @@ BOOST_AUTO_TEST_CASE(Setns) pid_t pid = lxcpp::fork(); if (pid == 0) { try { - lxcpp::setns(::getpid(), {Namespace::MNT, - Namespace::PID, - Namespace::UTS, - Namespace::IPC, - Namespace::NET - }); + lxcpp::setns(::getpid(), + CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWNET); ::_exit(TEST_PASSED); } catch(...) { ::_exit(ERROR); @@ -100,7 +89,7 @@ BOOST_AUTO_TEST_CASE(SetnsUserNamespace) pid_t pid = lxcpp::fork(); if (pid == 0) { try { - lxcpp::setns(::getpid(), {Namespace::USER}); + lxcpp::setns(::getpid(), CLONE_NEWUSER); ::_exit(ERROR); } catch(ProcessSetupException) { ::_exit(TEST_PASSED); -- 2.7.4 From 273e398513a0cddef7039acc046c265ace94b282 Mon Sep 17 00:00:00 2001 From: Lukasz Pawelczyk Date: Wed, 14 Oct 2015 13:43:41 +0200 Subject: [PATCH 16/16] Don't force compiler colors by default, add an option for that [Feature] Make it possible to compile without colors. [Cause] Compilation can be on a terminal (or non-terminal) that does not understand them. [Solution] Add a cmake option to force compiler colors, by default they are "auto" [Verification] cmake -DVASUM_BUILD_FORCE_COMPILER_COLORS=true/false Change-Id: I60376daa9801e01901af2f9f8d9d3dd5d6c18494 --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0bd22e5..c0c800c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,9 @@ INCLUDE(GNUInstallDirs) IF (( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND NOT (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)) OR ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always") + IF (VASUM_BUILD_FORCE_COMPILER_COLORS) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always") + ENDIF() ENDIF() ## Compiler flags, depending on the build type ################################# -- 2.7.4