From 9aebe3b497f5c36681afb0334c8d4595638ca42e Mon Sep 17 00:00:00 2001 From: Krzysztof Dynowski Date: Thu, 3 Sep 2015 09:34:37 +0200 Subject: [PATCH 01/16] lxcpp: network implementation (part 2) [Feature] Network implementation for lxcpp (create/destroy, add/del ip addr) [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: I1286efb4893b9b77bebf20f0e40b9fd06611e48b --- common/netlink/netlink-message.hpp | 1 + common/netlink/netlink.hpp | 1 + libs/config/CMakeLists.txt | 2 +- libs/lxcpp/commands/netcreate.cpp | 45 ++- libs/lxcpp/commands/netcreate.hpp | 75 ++++- libs/lxcpp/container-impl.cpp | 47 +-- libs/lxcpp/container-impl.hpp | 7 +- libs/lxcpp/container.hpp | 6 +- libs/lxcpp/network-config.cpp | 50 +++- libs/lxcpp/network-config.hpp | 143 ++++----- libs/lxcpp/network.cpp | 532 ++++++++++++++++++++-------------- libs/lxcpp/network.hpp | 268 +++++++++++++++-- tests/unit_tests/lxcpp/ut-network.cpp | 129 +++++++-- tests/unit_tests/server/ut-zone.cpp | 2 - 14 files changed, 912 insertions(+), 396 deletions(-) diff --git a/common/netlink/netlink-message.hpp b/common/netlink/netlink-message.hpp index 73e6a4c..3ee729b 100644 --- a/common/netlink/netlink-message.hpp +++ b/common/netlink/netlink-message.hpp @@ -35,6 +35,7 @@ #include #include +//FIXME remove from namespace vasum namespace vasum { namespace netlink { diff --git a/common/netlink/netlink.hpp b/common/netlink/netlink.hpp index 8d33957..56eff0b 100644 --- a/common/netlink/netlink.hpp +++ b/common/netlink/netlink.hpp @@ -28,6 +28,7 @@ #include #include +//FIXME remove from namespace vasum namespace vasum { namespace netlink { diff --git a/libs/config/CMakeLists.txt b/libs/config/CMakeLists.txt index 160cb09..81c8bbb 100644 --- a/libs/config/CMakeLists.txt +++ b/libs/config/CMakeLists.txt @@ -40,7 +40,7 @@ SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES PKG_CHECK_MODULES(CONFIG_DEPS REQUIRED sqlite3 glib-2.0) PKG_SEARCH_MODULE(JSON_C REQUIRED json json-c) -INCLUDE_DIRECTORIES(${LIBS_FOLDER}) +INCLUDE_DIRECTORIES(${LIBS_FOLDER} ${COMMON_FOLDER}) INCLUDE_DIRECTORIES(SYSTEM ${CONFIG_DEPS_INCLUDE_DIRS} ${JSON_C_INCLUDE_DIRS}) TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS} ${CONFIG_DEPS_LIBRARIES} ${JSON_C_LIBRARIES}) diff --git a/libs/lxcpp/commands/netcreate.cpp b/libs/lxcpp/commands/netcreate.cpp index 3c88d8e..56c1e2a 100644 --- a/libs/lxcpp/commands/netcreate.cpp +++ b/libs/lxcpp/commands/netcreate.cpp @@ -28,17 +28,46 @@ namespace lxcpp { void NetCreateAll::execute() { - for (const auto& i : mInterfaceConfigs) { - NetworkInterface networkInerface(mContainerPid, i.getZoneIf()); - networkInerface.create(i.getHostIf(), i.getType(), i.getMode()); + pid_t pid = mContainer.getInitPid(); + for (const auto& interface : mNetwork.getInterfaces()) { + NetworkInterface networkInterface(interface.getZoneIf(), pid); + networkInterface.create(interface.getType(), interface.getHostIf(), interface.getMode()); Attrs attrs; - for (const auto& a : i.getAddrList()) { - Attr attr; - NetworkInterface::convertInetAddr2Attr(a, attr); - attrs.push_back(attr); + if (interface.getMTU() > 0) { + attrs.push_back(Attr{AttrName::MTU, std::to_string(interface.getMTU())}); } - networkInerface.setAttrs(attrs); + if (!interface.getMACAddress().empty()) { + attrs.push_back(Attr{AttrName::MAC, interface.getMACAddress()}); + } + if (interface.getTxLength() > 0) { + attrs.push_back(Attr{AttrName::TXQLEN, std::to_string(interface.getTxLength())}); + } + networkInterface.setAttrs(attrs); + + for (const auto& addr : interface.getAddrList()) { + networkInterface.addInetAddr(addr); + } + } +} + +void NetInteraceCreate::execute() +{ + NetworkInterface networkInterface(mZoneIf, mContainer.getInitPid()); + networkInterface.create(mType, mHostIf, mMode); +} + +void NetInterfaceSetAttrs::execute() +{ + NetworkInterface networkInterface(mIfname, mContainer.getInitPid()); + networkInterface.setAttrs(mAttrs); +} + +void NetInterfaceAddInetAddr::execute() +{ + NetworkInterface networkInterface(mIfname, mContainer.getInitPid()); + for (const auto& a : mAddrList) { + networkInterface.addInetAddr(a); } } diff --git a/libs/lxcpp/commands/netcreate.hpp b/libs/lxcpp/commands/netcreate.hpp index 5d6e512..dec7288 100644 --- a/libs/lxcpp/commands/netcreate.hpp +++ b/libs/lxcpp/commands/netcreate.hpp @@ -26,6 +26,7 @@ #include "lxcpp/commands/command.hpp" #include "lxcpp/network-config.hpp" +#include "lxcpp/container.hpp" #include @@ -37,19 +38,81 @@ public: * Runs call in the container's context * * Creates and configures network interfaces in the container - * */ - NetCreateAll(pid_t containerPid, const std::vector& iflist) : - mContainerPid(containerPid), - mInterfaceConfigs(iflist) + NetCreateAll(const Container& container, const NetworkConfig& network) : + mContainer(container), + mNetwork(network) + { + } + + void execute(); + +private: + const Container& mContainer; + const NetworkConfig& mNetwork; +}; + +class NetInteraceCreate final: Command { +public: + NetInteraceCreate(const Container& container, + const std::string& zoneif, + const std::string& hostif, + InterfaceType type, + MacVLanMode mode=MacVLanMode::PRIVATE) : + mContainer(container), + mZoneIf(zoneif), + mHostIf(hostif), + mType(type), + mMode(mode) + { + } + + void execute(); + +private: + const Container& mContainer; + const std::string& mZoneIf; + const std::string& mHostIf; + InterfaceType mType; + MacVLanMode mMode; +}; + +class NetInterfaceSetAttrs final: Command { +public: + NetInterfaceSetAttrs(const Container& container, + const std::string& ifname, + const Attrs& attrs) : + mContainer(container), + mIfname(ifname), + mAttrs(attrs) + { + } + + void execute(); + +private: + const Container& mContainer; + const std::string& mIfname; + const Attrs& mAttrs; +}; + +class NetInterfaceAddInetAddr final: Command { +public: + NetInterfaceAddInetAddr(const Container& container, + const std::string& ifname, + const std::vector& addrList) : + mContainer(container), + mIfname(ifname), + mAddrList(addrList) { } void execute(); private: - const pid_t mContainerPid; - const std::vector& mInterfaceConfigs; + const Container& mContainer; + const std::string& mIfname; + const std::vector& mAddrList; }; } // namespace lxcpp diff --git a/libs/lxcpp/container-impl.cpp b/libs/lxcpp/container-impl.cpp index 5301cb5..c7035e4 100644 --- a/libs/lxcpp/container-impl.cpp +++ b/libs/lxcpp/container-impl.cpp @@ -230,9 +230,10 @@ const std::vector& ContainerImpl::getNamespaces() const void ContainerImpl::addInterfaceConfig(const std::string& hostif, const std::string& zoneif, InterfaceType type, + const std::vector& addrs, MacVLanMode mode) { - mConfig.mNetwork.addInterfaceConfig(hostif, zoneif, type, mode); + mConfig.mNetwork.addInterfaceConfig(hostif, zoneif, type, addrs, mode); } void ContainerImpl::addInetConfig(const std::string& ifname, const InetAddr& addr) @@ -247,10 +248,9 @@ std::vector ContainerImpl::getInterfaces() const NetworkInterfaceInfo ContainerImpl::getInterfaceInfo(const std::string& ifname) const { - NetworkInterface ni(getInitPid(), ifname); + NetworkInterface ni(ifname, getInitPid()); std::vector addrs; std::string macaddr; - InetAddr addr; int mtu = 0, flags = 0; Attrs attrs = ni.getAttrs(); for (const Attr& a : attrs) { @@ -261,11 +261,6 @@ NetworkInterfaceInfo ContainerImpl::getInterfaceInfo(const std::string& ifname) case AttrName::MTU: mtu = std::stoul(a.value); break; - case AttrName::IPV6: - case AttrName::IPV4: - NetworkInterface::convertAttr2InetAddr(a, addr); - addrs.push_back(addr); - break; case AttrName::FLAGS: flags = std::stoul(a.value); break; @@ -273,6 +268,7 @@ NetworkInterfaceInfo ContainerImpl::getInterfaceInfo(const std::string& ifname) break; } } + addrs = ni.getInetAddressList(); return NetworkInterfaceInfo{ifname, ni.status(), macaddr, mtu, flags, addrs}; } @@ -281,33 +277,44 @@ void ContainerImpl::createInterface(const std::string& hostif, InterfaceType type, MacVLanMode mode) { - NetworkInterface ni(getInitPid(), zoneif); - ni.create(hostif, type, mode); + NetworkInterface ni(zoneif, getInitPid()); + ni.create(type, hostif, mode); } -void ContainerImpl::destroyInterface(const std::string& /*ifname*/) +void ContainerImpl::destroyInterface(const std::string& ifname) { - throw NotImplementedException(); + NetworkInterface ni(ifname, getInitPid()); + ni.destroy(); } -void ContainerImpl::setUp(const std::string& /*ifname*/) +void ContainerImpl::moveInterface(const std::string& ifname) { - throw NotImplementedException(); + NetworkInterface ni(ifname); + ni.moveToContainer(getInitPid()); } -void ContainerImpl::setDown(const std::string& /*ifname*/) +void ContainerImpl::setUp(const std::string& ifname) { - throw NotImplementedException(); + NetworkInterface ni(ifname, getInitPid()); + ni.up(); } -void ContainerImpl::addAddr(const std::string& /*ifname*/, const InetAddr& /*addr*/) +void ContainerImpl::setDown(const std::string& ifname) { - throw NotImplementedException(); + NetworkInterface ni(ifname, getInitPid()); + ni.down(); } -void ContainerImpl::delAddr(const std::string& /*ifname*/, const InetAddr& /*addr*/) +void ContainerImpl::addInetAddr(const std::string& ifname, const InetAddr& addr) { - throw NotImplementedException(); + NetworkInterface ni(ifname, getInitPid()); + ni.addInetAddr(addr); +} + +void ContainerImpl::delInetAddr(const std::string& ifname, const InetAddr& addr) +{ + NetworkInterface ni(ifname, getInitPid()); + ni.delInetAddr(addr); } } // namespace lxcpp diff --git a/libs/lxcpp/container-impl.hpp b/libs/lxcpp/container-impl.hpp index b0786a1..7393974 100644 --- a/libs/lxcpp/container-impl.hpp +++ b/libs/lxcpp/container-impl.hpp @@ -30,7 +30,6 @@ #include "lxcpp/container-config.hpp" #include "lxcpp/container.hpp" #include "lxcpp/namespace.hpp" -#include "lxcpp/network.hpp" namespace lxcpp { @@ -77,6 +76,7 @@ public: void addInterfaceConfig(const std::string& hostif, const std::string& zoneif, InterfaceType type, + const std::vector& addrs, MacVLanMode mode); void addInetConfig(const std::string& ifname, const InetAddr& addr); @@ -87,11 +87,12 @@ public: const std::string& zoneif, InterfaceType type, MacVLanMode mode); + void moveInterface(const std::string& ifname); void destroyInterface(const std::string& ifname); void setUp(const std::string& ifname); void setDown(const std::string& ifname); - void addAddr(const std::string& ifname, const InetAddr& addr); - void delAddr(const std::string& ifname, const InetAddr& addr); + void addInetAddr(const std::string& ifname, const InetAddr& addr); + void delInetAddr(const std::string& ifname, const InetAddr& addr); private: ContainerConfig mConfig; diff --git a/libs/lxcpp/container.hpp b/libs/lxcpp/container.hpp index 96af842..51dda8c 100644 --- a/libs/lxcpp/container.hpp +++ b/libs/lxcpp/container.hpp @@ -78,6 +78,7 @@ public: virtual void addInterfaceConfig(const std::string& hostif, const std::string& zoneif, InterfaceType type, + const std::vector& addrs, MacVLanMode mode) = 0; virtual void addInetConfig(const std::string& ifname, const InetAddr& addr) = 0; @@ -89,10 +90,11 @@ public: InterfaceType type, MacVLanMode mode) = 0; virtual void destroyInterface(const std::string& ifname) = 0; + virtual void moveInterface(const std::string& ifname) = 0; virtual void setUp(const std::string& ifname) = 0; virtual void setDown(const std::string& ifname) = 0; - virtual void addAddr(const std::string& ifname, const InetAddr& addr) = 0; - virtual void delAddr(const std::string& ifname, const InetAddr& addr) = 0; + virtual void addInetAddr(const std::string& ifname, const InetAddr& addr) = 0; + virtual void delInetAddr(const std::string& ifname, const InetAddr& addr) = 0; }; } // namespace lxcpp diff --git a/libs/lxcpp/network-config.cpp b/libs/lxcpp/network-config.cpp index 17e1449..89019ec 100644 --- a/libs/lxcpp/network-config.cpp +++ b/libs/lxcpp/network-config.cpp @@ -23,12 +23,10 @@ */ #include "lxcpp/network-config.hpp" -#include "lxcpp/network.hpp" #include "lxcpp/exception.hpp" #include "logger/logger.hpp" #include - namespace lxcpp { const std::string& NetworkInterfaceConfig::getHostIf() const @@ -41,14 +39,44 @@ const std::string& NetworkInterfaceConfig::getZoneIf() const return mZoneIf; } -const InterfaceType& NetworkInterfaceConfig::getType() const +InterfaceType NetworkInterfaceConfig::getType() const +{ + return static_cast(mType); +} + +MacVLanMode NetworkInterfaceConfig::getMode() const +{ + return static_cast(mMode); +} + +void NetworkInterfaceConfig::setMTU(int mtu) +{ + mMtu = mtu; +} + +void NetworkInterfaceConfig::setMACAddress(const std::string& mac) { - return mType; + mMacAddress = mac; } -const MacVLanMode& NetworkInterfaceConfig::getMode() const +void NetworkInterfaceConfig::setTxLength(int txlen) { - return mMode; + mTxLength = txlen; +} + +int NetworkInterfaceConfig::getMTU() const +{ + return mMtu; +} + +const std::string& NetworkInterfaceConfig::getMACAddress() const +{ + return mMacAddress; +} + +int NetworkInterfaceConfig::getTxLength() const +{ + return mTxLength; } const std::vector& NetworkInterfaceConfig::getAddrList() const @@ -60,7 +88,7 @@ void NetworkInterfaceConfig::addInetAddr(const InetAddr& addr) { std::vector::iterator exists = std::find(mIpAddrList.begin(), mIpAddrList.end(), addr); if (exists != mIpAddrList.end()) { - std::string msg("Address already assigned"); + const std::string msg("Address already assigned"); throw NetworkException(msg); } mIpAddrList.push_back(addr); @@ -69,6 +97,7 @@ void NetworkInterfaceConfig::addInetAddr(const InetAddr& addr) void NetworkConfig::addInterfaceConfig(const std::string& hostif, const std::string& zoneif, InterfaceType type, + const std::vector& addrs, MacVLanMode mode) { auto it = std::find_if(mInterfaces.begin(), mInterfaces.end(), @@ -76,12 +105,13 @@ void NetworkConfig::addInterfaceConfig(const std::string& hostif, return entry.getZoneIf() == zoneif; } ); + if (it != mInterfaces.end()) { - std::string msg = "Interface already exists"; + const std::string msg = "Interface already exists"; LOGE(msg); throw NetworkException(msg); } - mInterfaces.push_back(NetworkInterfaceConfig(hostif,zoneif,type,mode)); + mInterfaces.push_back(NetworkInterfaceConfig(hostif, zoneif, type, addrs, mode)); } void NetworkConfig::addInetConfig(const std::string& ifname, const InetAddr& addr) @@ -93,7 +123,7 @@ void NetworkConfig::addInetConfig(const std::string& ifname, const InetAddr& add ); if (it == mInterfaces.end()) { - std::string msg = "No such interface"; + const std::string msg = "No such interface"; LOGE(msg); throw NetworkException(msg); } diff --git a/libs/lxcpp/network-config.hpp b/libs/lxcpp/network-config.hpp index c94f45b..aa1ee18 100644 --- a/libs/lxcpp/network-config.hpp +++ b/libs/lxcpp/network-config.hpp @@ -26,6 +26,9 @@ #include "config/config.hpp" #include "config/fields.hpp" +//#include "config/fields-union.hpp" +#include "lxcpp/network.hpp" +#include "lxcpp/exception.hpp" #include #include @@ -37,78 +40,25 @@ namespace lxcpp { /** - * Created interface type - */ -enum class InterfaceType { - VETH, - BRIDGE, - MACVLAN, - MOVE -}; - -/** - * Suported MacVLan modes - */ -enum class MacVLanMode { - PRIVATE, - VEPA, - BRIDGE, - PASSTHRU -}; - -/** - * Suported address types - */ -enum class InetAddrType { - IPV4, - IPV6 -}; - -enum class NetStatus { - DOWN, - UP -}; - - -/** - * Unified ip address - */ -struct InetAddr { - InetAddrType type; - uint32_t flags; - int prefix; - union { - struct in_addr ipv4; - struct in6_addr ipv6; - } addr; -}; - -static inline bool operator==(const InetAddr& a, const InetAddr& b) { - if (a.type == b.type && a.prefix == b.prefix) { - if (a.type == InetAddrType::IPV4) { - return ::memcmp(&a.addr.ipv4, &b.addr.ipv4, sizeof(a.addr.ipv4)) == 0; - } - if (a.type == InetAddrType::IPV6) { - return ::memcmp(&a.addr.ipv6, &b.addr.ipv6, sizeof(a.addr.ipv6)) == 0; - } - } - return false; -} - - -/** * Network interface configuration */ class NetworkInterfaceConfig { public: + NetworkInterfaceConfig() = default; // default constructor required by visitor + NetworkInterfaceConfig(const std::string& hostif, const std::string& zoneif, InterfaceType type, - MacVLanMode mode = MacVLanMode::PRIVATE) : + const std::vector& addrs, + MacVLanMode mode) : mHostIf(hostif), mZoneIf(zoneif), - mType(type), - mMode(mode) + mType(static_cast(type)), + mMode(static_cast(mode)), + mMtu(0), + mMacAddress(), + mTxLength(0), + mIpAddrList(addrs) { } @@ -116,48 +66,79 @@ public: const std::string& getZoneIf() const; - const InterfaceType& getType() const; + InterfaceType getType() const; - const MacVLanMode& getMode() const; + MacVLanMode getMode() const; + + void setMTU(int mtu); + int getMTU() const; + + void setMACAddress(const std::string& mac); + const std::string& getMACAddress() const; + + void setTxLength(int txlen); + int getTxLength() const; const std::vector& getAddrList() const; - void addInetAddr(const InetAddr&); + void addInetAddr(const InetAddr& addr); + + CONFIG_REGISTER + ( + mHostIf, + mZoneIf, + mType, + mMode, + mIpAddrList + ) private: - const std::string mHostIf; - const std::string mZoneIf; - const InterfaceType mType; - const MacVLanMode mMode; - //TODO mtu, macaddress, txqueue + std::string mHostIf; + std::string mZoneIf; + int mType; + int mMode; + /* - * above are interface parameters which can be read/modified: + * interface parameters which can be set after interface is created * MTU (Maximum Transmit Unit) is maximum length of link level packet in TCP stream * MAC address is also called hardware card address * TXQueue is transmit queue length * - * I think most usufull would be possibility to set MAC address, other have their - * well working defaults but can be tuned to make faster networking (especially localy) + * I think most useful would be possibility to set MAC address, other have their + * well working defaults but can be tuned to make faster networking (especially locally) */ + int mMtu; + std::string mMacAddress; + int mTxLength; + std::vector mIpAddrList; }; /** * Network interface configuration */ -struct NetworkConfig { - - //for convinience +class NetworkConfig { +public: + /** + * adds interface configuration. + * throws NetworkException if zoneif name already on list + */ void addInterfaceConfig(const std::string& hostif, const std::string& zoneif, InterfaceType type, - MacVLanMode mode); + const std::vector& addrs = std::vector(), + MacVLanMode mode = MacVLanMode::PRIVATE); void addInetConfig(const std::string& ifname, const InetAddr& addr); - std::vector mInterfaces; + const std::vector& getInterfaces() const { return mInterfaces; } + const NetworkInterfaceConfig& getInterface(int i) const { return mInterfaces.at(i); } + + CONFIG_REGISTER( + mInterfaces + ) - //TODO tmporary to allow serialization of this object - CONFIG_REGISTER_EMPTY +private: + std::vector mInterfaces; }; } //namespace lxcpp diff --git a/libs/lxcpp/network.cpp b/libs/lxcpp/network.cpp index 965f20c..1214e5c 100644 --- a/libs/lxcpp/network.cpp +++ b/libs/lxcpp/network.cpp @@ -28,12 +28,18 @@ #include "netlink/netlink-message.hpp" #include "utils/make-clean.hpp" #include "utils/text.hpp" +#include "utils/exception.hpp" #include "logger/logger.hpp" #include -#include +#include #include +#include +#include +#include +#include +#include #define CHANGE_FLAGS_DEFAULT 0xffffffff /* from linux/if_addr.h: @@ -52,35 +58,12 @@ using namespace vasum::netlink; namespace lxcpp { namespace { -std::string toString(const in_addr& addr) -{ - char buf[INET_ADDRSTRLEN]; - const char* ret = ::inet_ntop(AF_INET, &addr, buf, INET_ADDRSTRLEN); - if (ret == NULL) { - std::string msg = "Can't parse inet v4 addr"; - LOGE(msg); - throw NetworkException(msg); - } - return ret; -} - -std::string toString(const in6_addr& addr) -{ - char buf[INET6_ADDRSTRLEN]; - const char* ret = ::inet_ntop(AF_INET6, &addr, buf, INET6_ADDRSTRLEN); - if (ret == NULL) { - std::string msg = "Can't parse inet v6 addr"; - LOGE(msg); - throw NetworkException(msg); - } - return ret; -} uint32_t getInterfaceIndex(const std::string& name) { uint32_t index = ::if_nametoindex(name.c_str()); if (!index) { - std::string msg = "Can't find interface"; + const std::string msg = "Can't find interface " + utils::getSystemErrorMessage(); LOGE(msg); throw NetworkException(msg); } @@ -90,24 +73,35 @@ uint32_t getInterfaceIndex(const std::string& name) uint32_t getInterfaceIndex(const std::string& name, pid_t pid) { NetlinkMessage nlm(RTM_GETLINK, NLM_F_REQUEST | NLM_F_ACK); - ifinfomsg infoPeer = utils::make_clean(); - infoPeer.ifi_family = AF_UNSPEC; - infoPeer.ifi_change = CHANGE_FLAGS_DEFAULT; - nlm.put(infoPeer) + ifinfomsg info = utils::make_clean(); + info.ifi_family = AF_UNSPEC; + info.ifi_change = CHANGE_FLAGS_DEFAULT; + nlm.put(info) .put(IFLA_IFNAME, name); NetlinkResponse response = send(nlm, pid); if (!response.hasMessage()) { - std::string msg = "Can't get interface index"; + const std::string msg = "Can't get interface index"; LOGE(msg); throw NetworkException(msg); } - response.fetch(infoPeer); - return infoPeer.ifi_index; + response.fetch(info); + return info.ifi_index; +} + +void bridgeModify(const std::string& ifname, uint32_t masterIndex) { + NetlinkMessage nlm(RTM_SETLINK, NLM_F_REQUEST | NLM_F_ACK); + ifinfomsg info = utils::make_clean(); + info.ifi_family = AF_UNSPEC; + info.ifi_change = CHANGE_FLAGS_DEFAULT; + info.ifi_index = getInterfaceIndex(ifname); + nlm.put(info) + .put(IFLA_MASTER, masterIndex); + send(nlm); } -void getAddressAttrs(Attrs& attrs, int family, const std::string& ifname, pid_t pid) +void getAddressList(std::vector& addrs, int family, const std::string& ifname, pid_t pid) { uint32_t index = getInterfaceIndex(ifname, pid); NetlinkMessage nlm(RTM_GETADDR, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP); @@ -116,22 +110,17 @@ void getAddressAttrs(Attrs& attrs, int family, const std::string& ifname, pid_t nlm.put(infoAddr); NetlinkResponse response = send(nlm, pid); - if (!response.hasMessage()) { - return ; - } - - Attr attr; while (response.hasMessage()) { ifaddrmsg addrmsg; response.fetch(addrmsg); if (addrmsg.ifa_index == index) { InetAddr a; if (addrmsg.ifa_family == AF_INET6) { - a.type = InetAddrType::IPV6; + a.setType(InetAddrType::IPV6); } else if (addrmsg.ifa_family == AF_INET) { - a.type = InetAddrType::IPV4; + a.setType(InetAddrType::IPV4); } else { - std::string msg = "Unsupported inet family"; + const std::string msg = "Unsupported inet family"; LOGE(msg); throw NetworkException(msg); } @@ -153,14 +142,14 @@ void getAddressAttrs(Attrs& attrs, int family, const std::string& ifname, pid_t // fall thru case IFA_LOCAL: //2 if (addrmsg.ifa_family == AF_INET6) { - response.fetch(attrType, a.addr.ipv6); + response.fetch(attrType, a.getAddr()); } else if (addrmsg.ifa_family == AF_INET) { - response.fetch(attrType, a.addr.ipv4); + response.fetch(attrType, a.getAddr()); } else { LOGW("unsupported family " << addrmsg.ifa_family); response.skipAttribute(); } - hasLocal=true; + hasLocal = true; break; case IFA_FLAGS: //8 extended flags - overwrites addrmsg.ifa_flags @@ -180,8 +169,7 @@ void getAddressAttrs(Attrs& attrs, int family, const std::string& ifname, pid_t break; } } - NetworkInterface::convertInetAddr2Attr(a, attr); - attrs.push_back(attr); + addrs.push_back(a); } response.fetchNextMessage(); } @@ -189,146 +177,256 @@ void getAddressAttrs(Attrs& attrs, int family, const std::string& ifname, pid_t } // anonymous namespace -void NetworkInterface::create(const std::string& hostif, - InterfaceType type, +std::string toString(const in_addr& addr) +{ + char buf[INET_ADDRSTRLEN]; + const char* ret = ::inet_ntop(AF_INET, &addr, buf, INET_ADDRSTRLEN); + if (ret == NULL) { + const std::string msg = "Wrong inet v4 addr " + utils::getSystemErrorMessage(); + LOGE(msg); + throw NetworkException(msg); + } + return ret; +} + +std::string toString(const in6_addr& addr) +{ + char buf[INET6_ADDRSTRLEN]; + const char* ret = ::inet_ntop(AF_INET6, &addr, buf, INET6_ADDRSTRLEN); + if (ret == NULL) { + const std::string msg = "Wrong inet v6 addr " + utils::getSystemErrorMessage(); + LOGE(msg); + throw NetworkException(msg); + } + return ret; +} + +void fromString(const std::string& s, in_addr& addr) +{ + if (s.empty()) { + ::memset(&addr, 0, sizeof(addr)); + } + else if (::inet_pton(AF_INET, s.c_str(), &addr) != 1) { + const std::string msg = "Can't parse inet v4 addr " + utils::getSystemErrorMessage(); + LOGE(msg); + throw NetworkException(msg); + } +} + +void fromString(const std::string& s, in6_addr& addr) +{ + if (s == ":") { + ::memset(&addr, 0, sizeof(addr)); + } + else if (::inet_pton(AF_INET6, s.c_str(), &addr) != 1) { + const std::string msg = "Can't parse inet v6 addr " + utils::getSystemErrorMessage(); + LOGE(msg); + throw NetworkException(msg); + } +} + +std::string toString(const InetAddr& a) { + std::string opts = "/" + std::to_string(a.prefix); + if (a.getType() == InetAddrType::IPV6) { + return toString(a.getAddr()) + opts; + } + if (a.getType() == InetAddrType::IPV4) { + return toString(a.getAddr()) + opts; + } + return ""; +} + +InetAddr::InetAddr(uint32_t f, int p, const std::string& a) +{ + if (a.find(":") != std::string::npos) { + setType(InetAddrType::IPV6); + fromString(a, getAddr()); + } else { + setType(InetAddrType::IPV4); + fromString(a, getAddr()); + } + flags = f; + prefix = p; +} + +void NetworkInterface::create(InterfaceType type, + const std::string& peerif, MacVLanMode mode) { switch (type) { case InterfaceType::VETH: - createVeth(hostif); + createVeth(peerif); break; case InterfaceType::BRIDGE: - createBridge(hostif); + createBridge(); break; case InterfaceType::MACVLAN: - createMacVLan(hostif, mode); - break; - case InterfaceType::MOVE: - move(hostif); + createMacVLan(peerif, mode); break; default: throw NetworkException("Unsuported interface type"); } } -void NetworkInterface::createVeth(const std::string& /*hostif*/) +void NetworkInterface::createVeth(const std::string& peerif) { - throw NotImplementedException(); + NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK); + ifinfomsg info = utils::make_clean(); + info.ifi_family = AF_UNSPEC; + info.ifi_change = CHANGE_FLAGS_DEFAULT; + nlm.put(info) + .put(IFLA_IFNAME, mIfname) + .beginNested(IFLA_LINKINFO) + .put(IFLA_INFO_KIND, "veth") + .beginNested(IFLA_INFO_DATA) + .beginNested(VETH_INFO_PEER) + .put(info) + .put(IFLA_IFNAME, peerif) + .endNested() + .endNested() + .endNested(); + send(nlm); } -void NetworkInterface::createBridge(const std::string& /*hostif*/) +void NetworkInterface::createBridge() { - throw NotImplementedException(); + NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK); + ifinfomsg info = utils::make_clean(); + info.ifi_family = AF_UNSPEC; + info.ifi_change = CHANGE_FLAGS_DEFAULT; + nlm.put(info) + .beginNested(IFLA_LINKINFO) + .put(IFLA_INFO_KIND, "bridge") + .beginNested(IFLA_INFO_DATA) + .beginNested(IFLA_AF_SPEC) + .put(IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_MASTER) + .endNested() + .endNested() + .endNested() + .put(IFLA_IFNAME, mIfname); //bridge name (will be created) + send(nlm); } -void NetworkInterface::createMacVLan(const std::string& /*hostif*/, MacVLanMode /*mode*/) +void NetworkInterface::createMacVLan(const std::string& maserif, MacVLanMode mode) { - throw NotImplementedException(); + uint32_t index = getInterfaceIndex(maserif); + NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK); + ifinfomsg info = utils::make_clean(); + info.ifi_family = AF_UNSPEC; + info.ifi_change = CHANGE_FLAGS_DEFAULT; + nlm.put(info) + .beginNested(IFLA_LINKINFO) + .put(IFLA_INFO_KIND, "macvlan") + .beginNested(IFLA_INFO_DATA) + .put(IFLA_MACVLAN_MODE, static_cast(mode)) + .endNested() + .endNested() + .put(IFLA_LINK, index) //master index + .put(IFLA_IFNAME, mIfname); //slave name (will be created) + send(nlm); } -void NetworkInterface::move(const std::string& hostif) +void NetworkInterface::moveToContainer(pid_t pid) { - uint32_t index = getInterfaceIndex(hostif); - NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK); - ifinfomsg infopeer = utils::make_clean(); - infopeer.ifi_family = AF_UNSPEC; - infopeer.ifi_index = index; - nlm.put(infopeer) - .put(IFLA_NET_NS_PID, mContainerPid); - send(nlm); - - //rename to mIfname inside container - if (mIfname != hostif) { - renameFrom(hostif); - } + NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK); + ifinfomsg info = utils::make_clean(); + info.ifi_family = AF_UNSPEC; + info.ifi_index = getInterfaceIndex(mIfname); + nlm.put(info) + .put(IFLA_NET_NS_PID, pid); + send(nlm); + mContainerPid = pid; } void NetworkInterface::destroy() { - throw NotImplementedException(); + //uint32_t index = getInterfaceIndex(mIfname); + NetlinkMessage nlm(RTM_DELLINK, NLM_F_REQUEST | NLM_F_ACK); + ifinfomsg info = utils::make_clean(); + info.ifi_family = AF_UNSPEC; + info.ifi_change = CHANGE_FLAGS_DEFAULT; + info.ifi_index = getInterfaceIndex(mIfname, mContainerPid); + nlm.put(info) + .put(IFLA_IFNAME, mIfname); + send(nlm); } -NetStatus NetworkInterface::status() +NetStatus NetworkInterface::status() const { - throw NotImplementedException(); - /* - //TODO get container status, if stopped return CONFIGURED - if (mContainerPid<=0) return CONFIGURED; - // read netlink - return DOWN;*/ -} - + NetlinkMessage nlm(RTM_GETLINK, NLM_F_REQUEST | NLM_F_ACK); + ifinfomsg info = utils::make_clean(); + info.ifi_family = AF_UNSPEC; + info.ifi_change = CHANGE_FLAGS_DEFAULT; + nlm.put(info) + .put(IFLA_IFNAME, mIfname); -void NetworkInterface::up() -{ - throw NotImplementedException(); -} + NetlinkResponse response = send(nlm, mContainerPid); + if (!response.hasMessage()) { + throw NetworkException("Can't get interface information"); + } -void NetworkInterface::down() -{ - throw NotImplementedException(); + response.fetch(info); + return (info.ifi_flags & IFF_UP) != 0 ? NetStatus::UP : NetStatus::DOWN; } void NetworkInterface::renameFrom(const std::string& oldif) { NetlinkMessage nlm(RTM_SETLINK, NLM_F_REQUEST | NLM_F_ACK); - ifinfomsg infoPeer = utils::make_clean(); - infoPeer.ifi_family = AF_UNSPEC; - infoPeer.ifi_index = getInterfaceIndex(oldif, mContainerPid); - infoPeer.ifi_change = CHANGE_FLAGS_DEFAULT; + ifinfomsg info = utils::make_clean(); + info.ifi_family = AF_UNSPEC; + info.ifi_index = getInterfaceIndex(oldif, mContainerPid); + info.ifi_change = CHANGE_FLAGS_DEFAULT; - nlm.put(infoPeer) + nlm.put(info) .put(IFLA_IFNAME, mIfname); send(nlm, mContainerPid); } -void NetworkInterface::addInetAddr(const InetAddr& addr) +void NetworkInterface::addToBridge(const std::string& bridge) { - NetlinkMessage nlm(RTM_NEWADDR, NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK); - ifaddrmsg infoAddr = utils::make_clean(); - infoAddr.ifa_index = getInterfaceIndex(mIfname, mContainerPid); - infoAddr.ifa_family = addr.type == InetAddrType::IPV4 ? AF_INET : AF_INET6; - infoAddr.ifa_prefixlen = addr.prefix; - infoAddr.ifa_flags = addr.flags; - nlm.put(infoAddr); - - if (addr.type == InetAddrType::IPV6) { - nlm.put(IFA_ADDRESS, addr.addr.ipv6); - nlm.put(IFA_LOCAL, addr.addr.ipv6); - } else if (addr.type == InetAddrType::IPV4) { - nlm.put(IFA_ADDRESS, addr.addr.ipv4); - nlm.put(IFA_LOCAL, addr.addr.ipv4); - } + bridgeModify(mIfname, getInterfaceIndex(bridge)); +} - send(nlm, mContainerPid); +void NetworkInterface::delFromBridge() +{ + bridgeModify(mIfname, 0); } + void NetworkInterface::setAttrs(const Attrs& attrs) { + if (attrs.empty()) { + return ; + } + //TODO check this: NetlinkMessage nlm(RTM_SETLINK, NLM_F_REQUEST | NLM_F_ACK); NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK); - unsigned mtu=0, link=0, txq=0; - ifinfomsg infoPeer = utils::make_clean(); - infoPeer.ifi_family = AF_UNSPEC; - infoPeer.ifi_index = getInterfaceIndex(mIfname, mContainerPid); - infoPeer.ifi_change = CHANGE_FLAGS_DEFAULT; + ifinfomsg info = utils::make_clean(); + info.ifi_index = getInterfaceIndex(mIfname, mContainerPid); + info.ifi_family = AF_UNSPEC; + info.ifi_change = CHANGE_FLAGS_DEFAULT; + std::string mac; + unsigned mtu = 0, link = 0, txq = 0; for (const auto& attr : attrs) { if (attr.name == AttrName::FLAGS) { - infoPeer.ifi_flags = stoul(attr.value); + info.ifi_flags = stoul(attr.value); } else if (attr.name == AttrName::CHANGE) { - infoPeer.ifi_change = stoul(attr.value); + info.ifi_change = stoul(attr.value); } else if (attr.name == AttrName::TYPE) { - infoPeer.ifi_type = stoul(attr.value); + info.ifi_type = stoul(attr.value); } else if (attr.name == AttrName::MTU) { mtu = stoul(attr.value); } else if (attr.name == AttrName::LINK) { link = stoul(attr.value); } else if (attr.name == AttrName::TXQLEN) { txq = stoul(attr.value); + } else if (attr.name == AttrName::MAC) { + mac = attr.value; } + } - nlm.put(infoPeer); + nlm.put(info); if (mtu) { nlm.put(IFLA_MTU, mtu); } @@ -338,28 +436,23 @@ void NetworkInterface::setAttrs(const Attrs& attrs) if (txq) { nlm.put(IFLA_TXQLEN, txq); } + if (!mac.empty()) { + nlm.put(IFLA_ADDRESS, mac); + } NetlinkResponse response = send(nlm, mContainerPid); if (!response.hasMessage()) { throw NetworkException("Can't set interface information"); } - - // configure inet addresses - InetAddr addr; - for (const Attr& a : attrs) { - if (convertAttr2InetAddr(a, addr)) { - addInetAddr(addr); - } - } } Attrs NetworkInterface::getAttrs() const { NetlinkMessage nlm(RTM_GETLINK, NLM_F_REQUEST | NLM_F_ACK); - ifinfomsg infoPeer = utils::make_clean(); - infoPeer.ifi_family = AF_UNSPEC; - infoPeer.ifi_change = CHANGE_FLAGS_DEFAULT; - nlm.put(infoPeer) + ifinfomsg info = utils::make_clean(); + info.ifi_family = AF_UNSPEC; + info.ifi_change = CHANGE_FLAGS_DEFAULT; + nlm.put(info) .put(IFLA_IFNAME, mIfname); NetlinkResponse response = send(nlm, mContainerPid); @@ -368,9 +461,9 @@ Attrs NetworkInterface::getAttrs() const } Attrs attrs; - response.fetch(infoPeer); - attrs.push_back(Attr{AttrName::FLAGS, std::to_string(infoPeer.ifi_flags)}); - attrs.push_back(Attr{AttrName::TYPE, std::to_string(infoPeer.ifi_type)}); + response.fetch(info); + attrs.push_back(Attr{AttrName::FLAGS, std::to_string(info.ifi_flags)}); + attrs.push_back(Attr{AttrName::TYPE, std::to_string(info.ifi_type)}); while (response.hasAttribute()) { /* @@ -412,15 +505,100 @@ Attrs NetworkInterface::getAttrs() const break; } } - getAddressAttrs(attrs, AF_INET, mIfname, mContainerPid); - getAddressAttrs(attrs, AF_INET6, mIfname, mContainerPid); return attrs; } +void NetworkInterface::addInetAddr(const InetAddr& addr) +{ + NetlinkMessage nlm(RTM_NEWADDR, NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK); + ifaddrmsg infoAddr = utils::make_clean(); + infoAddr.ifa_index = getInterfaceIndex(mIfname, mContainerPid); + infoAddr.ifa_family = addr.getType() == InetAddrType::IPV4 ? AF_INET : AF_INET6; + infoAddr.ifa_prefixlen = addr.prefix; + infoAddr.ifa_flags = addr.flags; + nlm.put(infoAddr); + + if (addr.getType() == InetAddrType::IPV6) { + nlm.put(IFA_ADDRESS, addr.getAddr()); + nlm.put(IFA_LOCAL, addr.getAddr()); + } else if (addr.getType() == InetAddrType::IPV4) { + nlm.put(IFA_ADDRESS, addr.getAddr()); + nlm.put(IFA_LOCAL, addr.getAddr()); + } + + send(nlm, mContainerPid); +} + +void NetworkInterface::delInetAddr(const InetAddr& addr) +{ + NetlinkMessage nlm(RTM_DELADDR, NLM_F_REQUEST | NLM_F_ACK); + ifaddrmsg infoAddr = utils::make_clean(); + infoAddr.ifa_index = getInterfaceIndex(mIfname, mContainerPid); + infoAddr.ifa_family = addr.getType() == InetAddrType::IPV4 ? AF_INET : AF_INET6; + infoAddr.ifa_prefixlen = addr.prefix; + infoAddr.ifa_flags = addr.flags; + nlm.put(infoAddr); + + if (addr.getType() == InetAddrType::IPV6) { + nlm.put(IFA_ADDRESS, addr.getAddr()); + nlm.put(IFA_LOCAL, addr.getAddr()); + } else if (addr.getType() == InetAddrType::IPV4) { + nlm.put(IFA_ADDRESS, addr.getAddr()); + nlm.put(IFA_LOCAL, addr.getAddr()); + } + + send(nlm, mContainerPid); +} + +std::vector NetworkInterface::getInetAddressList() const +{ + std::vector addrs; + getAddressList(addrs, AF_UNSPEC, mIfname, mContainerPid); + return addrs; +} + +void NetworkInterface::up() +{ + Attrs attrs; + attrs.push_back(Attr{AttrName::CHANGE, std::to_string(IFF_UP)}); + attrs.push_back(Attr{AttrName::FLAGS, std::to_string(IFF_UP)}); + setAttrs(attrs); +} + +void NetworkInterface::down() +{ + Attrs attrs; + attrs.push_back(Attr{AttrName::CHANGE, std::to_string(IFF_UP)}); + attrs.push_back(Attr{AttrName::FLAGS, std::to_string(0)}); + setAttrs(attrs); +} + +void NetworkInterface::setMACAddress(const std::string& macaddr) +{ + Attrs attrs; + attrs.push_back(Attr{AttrName::MAC, macaddr}); + setAttrs(attrs); +} + +void NetworkInterface::setMTU(int mtu) +{ + Attrs attrs; + attrs.push_back(Attr{AttrName::MTU, std::to_string(mtu)}); + setAttrs(attrs); +} + +void NetworkInterface::setTxLength(int txqlen) +{ + Attrs attrs; + attrs.push_back(Attr{AttrName::TXQLEN, std::to_string(txqlen)}); + setAttrs(attrs); +} + + std::vector NetworkInterface::getInterfaces(pid_t initpid) { // get interfaces seen by netlink - NetlinkMessage nlm(RTM_GETLINK, NLM_F_REQUEST|NLM_F_DUMP|NLM_F_ROOT); + NetlinkMessage nlm(RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP | NLM_F_ROOT); ifinfomsg info = utils::make_clean(); info.ifi_family = AF_PACKET; nlm.put(info); @@ -439,78 +617,4 @@ std::vector NetworkInterface::getInterfaces(pid_t initpid) return iflist; } -void NetworkInterface::convertInetAddr2Attr(const InetAddr& a, Attr& attr) -{ - std::string value; - - if (a.type == InetAddrType::IPV6) { - value += "ip=" + toString(a.addr.ipv6); - } else if (a.type == InetAddrType::IPV4) { - value += "ip=" + toString(a.addr.ipv4); - } else { - throw NetworkException(); - } - - value += ",pfx=" + std::to_string(a.prefix); - value += ",flags=" + std::to_string(a.flags); - - if (a.type == InetAddrType::IPV6) { - attr = Attr{AttrName::IPV6, value}; - } else { - attr = Attr{AttrName::IPV4, value}; - } -} - -bool NetworkInterface::convertAttr2InetAddr(const Attr& attr, InetAddr& addr) -{ - std::string::size_type s = 0U; - std::string::size_type e = s; - - if (attr.name != AttrName::IPV6 && attr.name != AttrName::IPV4) { - return false; //not inet attribute - } - - addr.prefix = 0; - addr.flags = 0; - - bool addrFound = false; - while (e != std::string::npos) { - e = attr.value.find(',', s); - std::string ss = attr.value.substr(s, e - s); - s = e + 1; - - std::string name; - std::string value; - std::string::size_type p = ss.find('='); - if (p != std::string::npos) { - name = ss.substr(0, p); - value = ss.substr(p + 1); - } else { - name = ss; - //value remains empty - } - - if (name == "ip") { - if (attr.name == AttrName::IPV6) { - addr.type = InetAddrType::IPV6; - if (inet_pton(AF_INET6, value.c_str(), &addr.addr.ipv6) != 1) { - throw NetworkException("Parse IPV6 address"); - } - } else if (attr.name == AttrName::IPV4) { - addr.type = InetAddrType::IPV4; - if (inet_pton(AF_INET, value.c_str(), &addr.addr.ipv4) != 1) { - throw NetworkException("Parse IPV4 address"); - } - } - addrFound = true; - } else if (name == "pfx") { - addr.prefix = stoul(value); - } else if (name == "flags") { - addr.flags = stoul(value); - } - } - - return addrFound; -} - } // namespace lxcpp diff --git a/libs/lxcpp/network.hpp b/libs/lxcpp/network.hpp index cc0167e..a1ebf49 100644 --- a/libs/lxcpp/network.hpp +++ b/libs/lxcpp/network.hpp @@ -24,14 +24,125 @@ #ifndef LXCPP_NETWORK_HPP #define LXCPP_NETWORK_HPP -#include "lxcpp/network-config.hpp" +#include "config/fields.hpp" +#include #include #include #include +#include + namespace lxcpp { +std::string toString(const in_addr& addr); +std::string toString(const in6_addr& addr); +void fromString(const std::string& s, in_addr& addr); +void fromString(const std::string& s, in6_addr& addr); + +/** + * Suported address types + */ +enum class InetAddrType { + IPV4, + IPV6 +}; + +/** + * Unified ip address + */ +class InetAddr { +public: + InetAddr() {} + InetAddr(uint32_t flags, int prefix, const std::string& addr); + + InetAddrType getType() const { + return static_cast(type); + } + void setType(InetAddrType t) { + type = static_cast(t); + } + + template + T& getAddr() { + //FIXME return union field after fix of addr type + char *v = addr; + return *(reinterpret_cast(v)); + } + template + const T& getAddr() const { + //FIXME return union field after fix of addr type + const char *v = addr; + return *(reinterpret_cast(v)); + } + + uint32_t flags; + int prefix; + + CONFIG_REGISTER + ( + type, + flags, + prefix + //FIXME add when visitor can serialize char[SIZE] + //addr + ) + +private: + //FIXME change to union when visitor can serialize type by istream ostream operators + char addr[sizeof(in6_addr)]; + //FIXME: change to enum when visitor can serialize type by istream ostream operators + int type; +}; + +static inline bool operator==(const in_addr& a, const in_addr& b) +{ + return ::memcmp(&a, &b, sizeof(a)) == 0; +} + +static inline bool operator==(const in6_addr& a, const in6_addr& b) +{ + return ::memcmp(&a, &b, sizeof(a)) == 0; +} + +static inline bool operator==(const InetAddr& a, const InetAddr& b) +{ + if (a.getType() == b.getType() && a.prefix == b.prefix) { + if (a.getType() == InetAddrType::IPV6) + return a.getAddr() == b.getAddr(); + else + return a.getAddr() == b.getAddr(); + } + return false; +} + +std::string toString(const InetAddr& a); + +enum class RoutingTable { + UNSPEC, // also means 'any' + COMPAT, + DEFAULT, + MAIN, + LOCAL, + USER, +}; +inline std::string toString(RoutingTable rt) { + switch (rt) { + case RoutingTable::UNSPEC: + return "unspec"; + case RoutingTable::COMPAT: + return "compat"; + case RoutingTable::DEFAULT: + return "default"; + case RoutingTable::MAIN: + return "main"; + case RoutingTable::LOCAL: + return "local"; + default: + return "user"; + } +} + enum class AttrName { MAC, FLAGS, @@ -40,8 +151,6 @@ enum class AttrName { MTU, LINK, TXQLEN, - IPV4, - IPV6, }; inline std::ostream& operator<<(std::ostream& os, const AttrName& a) { @@ -53,8 +162,6 @@ inline std::ostream& operator<<(std::ostream& os, const AttrName& a) { case AttrName::MTU: os << "mtu"; break; case AttrName::LINK: os << "link"; break; case AttrName::TXQLEN: os << "txq"; break; - case AttrName::IPV4: os << "ipv4"; break; - case AttrName::IPV6: os << "ipv6"; break; } return os; } @@ -67,62 +174,165 @@ struct Attr { typedef std::vector Attrs; /** + * Created interface type + */ +enum class InterfaceType : int { + VETH, + BRIDGE, + MACVLAN +}; + +/** + * Suported MacVLan modes + */ +enum class MacVLanMode { + PRIVATE, + VEPA, + BRIDGE, + PASSTHRU +}; + +enum class NetStatus { + DOWN, + UP +}; + +std::string toString(const InetAddr& a); + +/** * Network operations to be performed on given container and interface * operates on netlink device */ class NetworkInterface { public: /** - * Create network interface object for the ifname in the container + * Create network interface object for the ifname in the container (network namespace) + * Note: pid=0 is kernel */ - NetworkInterface(pid_t pid, const std::string& ifname) : - mContainerPid(pid), - mIfname(ifname) + NetworkInterface(const std::string& ifname, pid_t pid = 0) : + mIfname(ifname), + mContainerPid(pid) { } const std::string& getName() const { return mIfname; } - //Network actions on Container - void create(const std::string& hostif, InterfaceType type, MacVLanMode mode=MacVLanMode::PRIVATE); + /** + * Retrieve network interface status (UP or DOWN) + */ + NetStatus status() const; + + /** + * Create network interface in container identified by mContainerPid + * Equivalent to: ip link add @mIfname type @type [...] + * Create pair of virtual ethernet interfaces + * ip link add @mIfname type veth peer name @peerif + * 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] + */ + void create(InterfaceType type, const std::string& peerif, MacVLanMode mode = MacVLanMode::PRIVATE); + + /** + * Delete interface + * Equivalent to: ip link delete @mIfname + */ void destroy(); - NetStatus status(); - void up(); - void down(); + /** + * Move interface to container + * Equivalent to: ip link set dev @hostif netns @mContainerPid + */ + void moveToContainer(pid_t p); /** * Rename interface name - * Equivalent to: ip link set dev $oldif name $this.mIfname + * Equivalent to: ip link set dev @oldif name @mIfname */ void renameFrom(const std::string& oldif); + /** + * Add interface to the bridge + * Equivalent to: ip link set @mIfname master @bridge + */ + void addToBridge(const std::string& bridge); + + /** + * Remove insterface from the bridge + * Equivalent to: ip link set @mIfname nomaster + */ + void delFromBridge(); + + /** + * Set or get interface attributes in one netlink call. + * Supported attributes: see @AttrNames + */ void setAttrs(const Attrs& attrs); Attrs getAttrs() const; - void setMACAddress(const std::string& macaddr); - void setMTU(int mtu); - void setTxLength(int txlen); + /** + * Add inet address to the interface + * Equivalent to: ip addr add @addr dev @mIfname + */ void addInetAddr(const InetAddr& addr); + + /** + * Remove inet address from the interface + * Equivalent to: ip addr del @addr dev @mIfname + */ void delInetAddr(const InetAddr& addr); - static std::vector getInterfaces(pid_t initpid); + /** + * Retrieve all inet addresses for the interface + * Equivalent to: ip addr show, ip -6 addr show + */ + std::vector getInetAddressList() const; - static void convertInetAddr2Attr(const InetAddr& addr, Attr& attr); - static bool convertAttr2InetAddr(const Attr& attr, InetAddr& addr); + /** + * Set interface up + * Equivalent to: ip link set @mInface up + */ + void up(); -private: - void createVeth(const std::string& hostif); - void createBridge(const std::string& hostif); - void createMacVLan(const std::string& hostif, MacVLanMode mode); /** - * Move interface to container - * Equivalent to: ip link set dev $hostif netns $mContainerPid + * Set interface down + * Equivalent to: ip link set @mInface down */ - void move(const std::string& hostif); + void down(); + + /** + * Set MAC address attribute + * Equivalent to: ip link set @mIface address @macaddr + * @macaddr in format AA:BB:CC:DD:FF:GG + */ + void setMACAddress(const std::string& macaddr); + + /** + * Set MTU attribute + * Equivalent to: ip link set @mIface mtu @mtu + */ + void setMTU(int mtu); + + /** + * Set TxQ attribute + * Equivalent to: ip link set @mIface txqueue @txlen + */ + void setTxLength(int txlen); + + /** + * Get list of network interafece names + * Equivalent to: ip link show + */ + static std::vector getInterfaces(pid_t initpid); + +private: + void createVeth(const std::string& peerif); + void createBridge(); + void createMacVLan(const std::string& masterif, MacVLanMode mode); - pid_t mContainerPid; ///< Container pid to operate on (0=kernel) const std::string mIfname; ///< network interface name inside zone + pid_t mContainerPid; ///< Container pid to operate on (0 means kernel) }; } // namespace lxcpp diff --git a/tests/unit_tests/lxcpp/ut-network.cpp b/tests/unit_tests/lxcpp/ut-network.cpp index 373ec1b..f797a6c 100644 --- a/tests/unit_tests/lxcpp/ut-network.cpp +++ b/tests/unit_tests/lxcpp/ut-network.cpp @@ -23,17 +23,32 @@ #include "config.hpp" #include "config/manager.hpp" -#include "lxcpp/network.hpp" +#include "lxcpp/network-config.hpp" #include "ut.hpp" #include #include +#include + namespace { +using namespace lxcpp; +using namespace config; + struct Fixture { Fixture() {} ~Fixture() {} + + static std::string getUniqueName(const std::string& prefix) { + std::vector iflist = NetworkInterface::getInterfaces(0); + std::string name; + unsigned i = 0; + do { + name = prefix + std::to_string(i++); + } while (std::find(iflist.begin(), iflist.end(), name) != iflist.end()); + return name; + } }; } // namespace @@ -46,38 +61,112 @@ struct Fixture { BOOST_FIXTURE_TEST_SUITE(LxcppNetworkSuite, Fixture) -using namespace lxcpp; - -BOOST_AUTO_TEST_CASE(ListInterfaces) +BOOST_AUTO_TEST_CASE(NetworkListInterfaces) { std::vector iflist; - BOOST_CHECK_NO_THROW(iflist = NetworkInterface::getInterfaces(0);) - for (const auto& i : iflist) { - std::cout << i << std::endl; - } -} + BOOST_CHECK_NO_THROW(iflist = NetworkInterface::getInterfaces(0)); -BOOST_AUTO_TEST_CASE(DetailedListInterfaces) -{ - std::vector iflist; - BOOST_CHECK_NO_THROW(iflist = NetworkInterface::getInterfaces(0);) for (const auto& i : iflist) { - NetworkInterface ni(1,i); - std::cout << "Attrs of " << i << std::endl; + NetworkInterface ni(i); + std::cout << i << ": "; Attrs attrs; - BOOST_CHECK_NO_THROW(attrs = ni.getAttrs();) + std::vector addrs; + BOOST_CHECK_NO_THROW(attrs = ni.getAttrs()); for (const Attr& a : attrs) { - std::cout << a.name << "=" << a.value << "; "; + std::cout << a.name << "=" << a.value; + if (a.name == AttrName::FLAGS) { + uint32_t f = stoul(a.value); + std::cout << "("; + std::cout << ((f & IFF_UP) !=0 ? "UP" : "DONW"); + std::cout << ")"; + } + std::cout << "; "; } std::cout << std::endl; + + BOOST_CHECK_NO_THROW(addrs = ni.getInetAddressList()); + for (const InetAddr& a : addrs) { + std::cout << " " << toString(a) << std::endl; + } } } + BOOST_AUTO_TEST_CASE(NetworkConfigSerialization) { + std::string tmpConfigFile = "/tmp/netconfig.conf"; NetworkConfig cfg; - std::string json; - BOOST_CHECK_NO_THROW(json = config::saveToJsonString(cfg);) - std::cout << json << std::endl; + BOOST_CHECK_NO_THROW(config::saveToJsonString(cfg)); + + cfg.addInterfaceConfig("host-veth0", "zone-eth0", InterfaceType::VETH); + cfg.addInterfaceConfig("host-veth1", "zone-eth1", InterfaceType::BRIDGE); + cfg.addInterfaceConfig("host-veth2", "zone-eth2", InterfaceType::MACVLAN); + + InetAddr addr(0, 24, "1.2.3.4"); + cfg.addInetConfig("zone-eth0", addr); + + config::saveToJsonFile(tmpConfigFile, cfg); + + NetworkConfig cfg2; + config::loadFromJsonFile(tmpConfigFile, cfg2); + + int ifnum = cfg.getInterfaces().size(); + for (int i = 0; i < ifnum; ++i) { + const NetworkInterfaceConfig& ni1 = cfg.getInterface(i); + const NetworkInterfaceConfig& ni2 = cfg2.getInterface(i); + + BOOST_CHECK_EQUAL(ni1.getHostIf(), ni2.getHostIf()); + BOOST_CHECK_EQUAL(ni1.getZoneIf(), ni2.getZoneIf()); + BOOST_CHECK(ni1.getType() == ni2.getType()); + BOOST_CHECK(ni1.getMode() == ni2.getMode()); + } +} +BOOST_AUTO_TEST_CASE(NetworkBridgeCreateDestroy) +{ + std::string name = getUniqueName("lolo"); + NetworkInterface ni(name); + InetAddr myip(0, 32, "10.100.1.1"); + + BOOST_CHECK_NO_THROW(ni.create(InterfaceType::BRIDGE, "")); + BOOST_CHECK_NO_THROW(ni.addInetAddr(myip);) + + std::vector iflist = NetworkInterface::getInterfaces(0); + BOOST_CHECK(std::find(iflist.begin(), iflist.end(), name) != iflist.end()); + + std::vector addrs = ni.getInetAddressList(); + BOOST_CHECK(std::find(addrs.begin(), addrs.end(), myip) != addrs.end()); + for (const auto& i : iflist) { + std::cout << " " << i; + } + std::cout << std::endl; + for (const InetAddr& a : addrs) { + std::cout << " " << toString(a) << std::endl; + } + + BOOST_CHECK_NO_THROW(ni.delInetAddr(myip)); + BOOST_CHECK_NO_THROW(ni.destroy()); +} + +BOOST_AUTO_TEST_CASE(NetworkMacVLanCreateDestroy) +{ + std::string masterif; + std::vector iflist = NetworkInterface::getInterfaces(0); + for (const auto& ifn : iflist) { + if (ifn == "lo") continue; + NetworkInterface n(ifn); + if (n.status() == NetStatus::UP) { + masterif = ifn; + break; + } + } + + NetworkInterface ni(getUniqueName("lolo")); + std::cout << " creating MACVLAN on " << masterif << std::endl; + BOOST_CHECK_NO_THROW(ni.create(InterfaceType::MACVLAN, masterif, MacVLanMode::VEPA)); + + iflist = NetworkInterface::getInterfaces(0); + BOOST_CHECK(std::find(iflist.begin(), iflist.end(), ni.getName()) != iflist.end()); + + BOOST_CHECK_NO_THROW(ni.destroy()); } BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/unit_tests/server/ut-zone.cpp b/tests/unit_tests/server/ut-zone.cpp index aefb29a..c7740e9 100644 --- a/tests/unit_tests/server/ut-zone.cpp +++ b/tests/unit_tests/server/ut-zone.cpp @@ -42,8 +42,6 @@ #include #include #include -#include -#include #include using namespace utils; -- 2.7.4 From e85426229880fbc634cfde33f35f1bce2b1eb32c Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Mon, 28 Sep 2015 18:19:07 +0200 Subject: [PATCH 02/16] config: Cstring serialization. [Feature] Serialization of null-terminated strings [Cause] N/A [Solution] Memory for serialized data is allocated with new, user should delete[] the field in the destructor or when it's not used. [Verification] Build, install, run tests. Change-Id: Iff49e14ab95410bd7a80ee23abf04e9d92e65a55 --- libs/config/from-fdstore-visitor.hpp | 10 +++++++ libs/config/from-gvariant-visitor.hpp | 12 +++++++++ libs/config/from-json-visitor.hpp | 11 ++++++++ libs/config/from-kvjson-visitor.hpp | 10 +++++++ libs/config/kvstore.cpp | 37 ++++++++++++++++++++++++-- libs/config/kvstore.hpp | 2 ++ libs/config/to-fdstore-visitor.hpp | 8 ++++++ libs/config/to-gvariant-visitor.hpp | 1 + libs/config/to-json-visitor.hpp | 5 ++++ tests/unit_tests/config/testconfig-example.hpp | 4 +++ tests/unit_tests/config/ut-configuration.cpp | 1 + 11 files changed, 99 insertions(+), 2 deletions(-) diff --git a/libs/config/from-fdstore-visitor.hpp b/libs/config/from-fdstore-visitor.hpp index 3863f7f..a948b3b 100644 --- a/libs/config/from-fdstore-visitor.hpp +++ b/libs/config/from-fdstore-visitor.hpp @@ -61,6 +61,16 @@ private: mStore.read(&value.front(), size); } + void readInternal(char* &value) + { + size_t size; + readInternal(size); + + value = new char[size + 1]; + mStore.read(value, size); + value[size] = '\0'; + } + void readInternal(config::FileDescriptor& fd) { fd = mStore.receiveFD(); diff --git a/libs/config/from-gvariant-visitor.hpp b/libs/config/from-gvariant-visitor.hpp index e648625..a4134b6 100644 --- a/libs/config/from-gvariant-visitor.hpp +++ b/libs/config/from-gvariant-visitor.hpp @@ -127,6 +127,18 @@ private: value = g_variant_get_string(object, NULL); } + static void fromGVariant(GVariant* object, char* &value) + { + checkType(object, G_VARIANT_TYPE_STRING); + + const char* source = g_variant_get_string(object, NULL); + size_t len = std::strlen(source); + + value = new char[len + 1]; + std::strncpy(value, source, len); + value[len] = '\0'; + } + static void fromGVariant(GVariant* object, config::FileDescriptor& value) { checkType(object, G_VARIANT_TYPE_INT32); diff --git a/libs/config/from-json-visitor.hpp b/libs/config/from-json-visitor.hpp index 74d309b..bc7864a 100644 --- a/libs/config/from-json-visitor.hpp +++ b/libs/config/from-json-visitor.hpp @@ -30,6 +30,7 @@ #include #include +#include #include namespace config { @@ -137,6 +138,16 @@ private: value = json_object_get_string(object); } + static void fromJsonObject(json_object* object, char* &value) + { + checkType(object, json_type_string); + + int len = json_object_get_string_len(object); + value = new char[len + 1]; + std::strncpy(value, json_object_get_string(object), len); + value[len] = '\0'; + } + template static void fromJsonObject(json_object* object, std::vector& value) { diff --git a/libs/config/from-kvjson-visitor.hpp b/libs/config/from-kvjson-visitor.hpp index eea975c..cc6e14a 100644 --- a/libs/config/from-kvjson-visitor.hpp +++ b/libs/config/from-kvjson-visitor.hpp @@ -273,6 +273,16 @@ private: checkType(object, json_type_string); value = json_object_get_string(object); } + + static void fromJsonObject(json_object* object, char* &value) + { + checkType(object, json_type_string); + + int len = json_object_get_string_len(object); + value = new char[len + 1]; + std::strncpy(value, json_object_get_string(object), len); + value[len] = '\0'; + } }; } // namespace config diff --git a/libs/config/kvstore.cpp b/libs/config/kvstore.cpp index b6d922e..25febf6 100644 --- a/libs/config/kvstore.cpp +++ b/libs/config/kvstore.cpp @@ -32,6 +32,7 @@ #include #include #include +#include namespace config { @@ -231,13 +232,13 @@ void KVStore::remove(const std::string& key) transaction.commit(); } -void KVStore::setInternal(const std::string& key, const std::string& value) +void KVStore::setInternal(const std::string& key, const char* value) { Transaction transaction(*this); ScopedReset scopedReset(mSetValueStmt); ::sqlite3_bind_text(mSetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC); - ::sqlite3_bind_text(mSetValueStmt->get(), 2, value.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC); + ::sqlite3_bind_text(mSetValueStmt->get(), 2, value, AUTO_DETERM_SIZE, SQLITE_STATIC); if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) { @@ -246,6 +247,11 @@ void KVStore::setInternal(const std::string& key, const std::string& value) transaction.commit(); } +void KVStore::setInternal(const std::string& key, const std::string& value) +{ + setInternal(key, value.c_str()); +} + void KVStore::setInternal(const std::string& key, const std::initializer_list& values) { setInternal(key, std::vector(values)); @@ -294,6 +300,33 @@ std::string KVStore::getInternal(const std::string& key, std::string*) return value; } +char* KVStore::getInternal(const std::string& key, char**) +{ + Transaction transaction(*this); + ScopedReset scopedReset(mGetValueStmt); + + ::sqlite3_bind_text(mGetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT); + + int ret = ::sqlite3_step(mGetValueStmt->get()); + if (ret == SQLITE_DONE) { + throw ConfigException("No value corresponding to the key: " + key + "@" + mPath); + } + if (ret != SQLITE_ROW) { + throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); + } + + const char* source = reinterpret_cast(sqlite3_column_text(mGetValueStmt->get(), FIRST_COLUMN)); + + size_t length = std::strlen(source); + char* value = new char[length + 1]; + + std::strncpy(value, source, length); + value[length] = '\0'; + + transaction.commit(); + return value; +} + std::vector KVStore::getInternal(const std::string& key, std::vector*) { Transaction transaction(*this); diff --git a/libs/config/kvstore.hpp b/libs/config/kvstore.hpp index eaf568b..583d534 100644 --- a/libs/config/kvstore.hpp +++ b/libs/config/kvstore.hpp @@ -134,6 +134,7 @@ private: bool mIsTransactionCommited; void setInternal(const std::string& key, const std::string& value); + 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 @@ -142,6 +143,7 @@ private: void setInternal(const std::string& key, const std::vector& values); 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 T getInternal(const std::string& key, T*); diff --git a/libs/config/to-fdstore-visitor.hpp b/libs/config/to-fdstore-visitor.hpp index 1eedddb..7da3d76 100644 --- a/libs/config/to-fdstore-visitor.hpp +++ b/libs/config/to-fdstore-visitor.hpp @@ -30,6 +30,7 @@ #include "config/types.hpp" #include +#include namespace config { @@ -60,6 +61,13 @@ private: mStore.write(value.c_str(), value.size()); } + void writeInternal(const char* &value) + { + size_t size = std::strlen(value); + writeInternal(size); + mStore.write(value, size); + } + void writeInternal(const config::FileDescriptor& fd) { mStore.sendFD(fd.value); diff --git a/libs/config/to-gvariant-visitor.hpp b/libs/config/to-gvariant-visitor.hpp index 047aa17..40c002d 100644 --- a/libs/config/to-gvariant-visitor.hpp +++ b/libs/config/to-gvariant-visitor.hpp @@ -83,6 +83,7 @@ private: 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 diff --git a/libs/config/to-json-visitor.hpp b/libs/config/to-json-visitor.hpp index 4ab4a4b..717b952 100644 --- a/libs/config/to-json-visitor.hpp +++ b/libs/config/to-json-visitor.hpp @@ -120,6 +120,11 @@ private: return json_object_new_string(value.c_str()); } + static json_object* toJsonObject(char* value) + { + return json_object_new_string(value); + } + template static json_object* toJsonObject(const std::vector& value) { diff --git a/tests/unit_tests/config/testconfig-example.hpp b/tests/unit_tests/config/testconfig-example.hpp index 549985b..949239d 100644 --- a/tests/unit_tests/config/testconfig-example.hpp +++ b/tests/unit_tests/config/testconfig-example.hpp @@ -88,6 +88,7 @@ struct TestConfig { std::uint32_t uint32Val; std::uint64_t uint64Val; std::string stringVal; + char* cstringVal; double doubleVal; bool boolVal; @@ -110,6 +111,7 @@ struct TestConfig { uint32Val, uint64Val, stringVal, + cstringVal, doubleVal, boolVal, @@ -150,6 +152,7 @@ const std::string jsonTestString = "\"uint32Val\": 123456, " "\"uint64Val\": 1234567890123456789, " "\"stringVal\": \"blah\", " + "\"cstringVal\": \"blah\", " "\"doubleVal\": -1.234000, " "\"boolVal\": true, " "\"emptyIntVector\": [ ], " @@ -173,6 +176,7 @@ const std::string jsonEmptyTestString = "\"uint32Val\": 0, " "\"uint64Val\": 0, " "\"stringVal\": \"\", " + "\"cstringVal\": \"\", " "\"boolVal\": false, " "\"emptyIntVector\": [ ], " "\"intVector\": [ ], " diff --git a/tests/unit_tests/config/ut-configuration.cpp b/tests/unit_tests/config/ut-configuration.cpp index 88b6a4e..68311eb 100644 --- a/tests/unit_tests/config/ut-configuration.cpp +++ b/tests/unit_tests/config/ut-configuration.cpp @@ -64,6 +64,7 @@ BOOST_AUTO_TEST_CASE(FromJsonString) BOOST_CHECK_EQUAL(123456, testConfig.uint32Val); BOOST_CHECK_EQUAL(1234567890123456789ll, testConfig.uint64Val); BOOST_CHECK_EQUAL("blah", testConfig.stringVal); + BOOST_CHECK_EQUAL("blah", testConfig.cstringVal); BOOST_CHECK_CLOSE(-1.234, testConfig.doubleVal, TOLERANCE); BOOST_CHECK_EQUAL(true, testConfig.boolVal); -- 2.7.4 From 1ce1ce12ca76938b17b1b83f5f6443be417156da Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Tue, 29 Sep 2015 17:41:18 +0200 Subject: [PATCH 03/16] config: std::array serialization. [Feature] Serialization of std::array [Cause] N/A [Solution] N/A [Verification] Build, install, run tests. Change-Id: I3e2f890d07ef6c0818135566eadb11a025438fd7 --- libs/config/from-fdstore-visitor.hpp | 10 ++++++++ libs/config/from-gvariant-visitor.hpp | 15 ++++++++++++ libs/config/from-json-visitor.hpp | 17 ++++++++++--- libs/config/from-kvjson-visitor.hpp | 34 ++++++++++++++++++++++++++ libs/config/kvstore.hpp | 31 +++++++++++++++++++++++ libs/config/to-fdstore-visitor.hpp | 14 +++++++++-- libs/config/to-gvariant-visitor.hpp | 15 ++++++++++++ libs/config/to-json-visitor.hpp | 10 ++++++++ tests/unit_tests/config/testconfig-example.hpp | 6 +++++ tests/unit_tests/config/ut-configuration.cpp | 10 +++++--- 10 files changed, 154 insertions(+), 8 deletions(-) diff --git a/libs/config/from-fdstore-visitor.hpp b/libs/config/from-fdstore-visitor.hpp index a948b3b..1013a61 100644 --- a/libs/config/from-fdstore-visitor.hpp +++ b/libs/config/from-fdstore-visitor.hpp @@ -29,7 +29,9 @@ #include "config/fdstore.hpp" #include "config/types.hpp" +#include #include +#include namespace config { @@ -100,6 +102,14 @@ private: readInternal(value); } } + + template + void readInternal(std::array& values) + { + for (T& value : values) { + readInternal(value); + } + } }; } // namespace config diff --git a/libs/config/from-gvariant-visitor.hpp b/libs/config/from-gvariant-visitor.hpp index a4134b6..213f917 100644 --- a/libs/config/from-gvariant-visitor.hpp +++ b/libs/config/from-gvariant-visitor.hpp @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -160,6 +161,20 @@ private: } } + template + static void fromGVariant(GVariant* object, std::array& values) + { + checkType(object, G_VARIANT_TYPE_ARRAY); + + GVariantIter iter; + g_variant_iter_init(&iter, object); + + for (T& value: values) { + auto child = makeUnique(g_variant_iter_next_value(&iter)); + fromGVariant(child.get(), 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 bc7864a..041027e 100644 --- a/libs/config/from-json-visitor.hpp +++ b/libs/config/from-json-visitor.hpp @@ -32,6 +32,7 @@ #include #include #include +#include namespace config { @@ -149,13 +150,23 @@ private: } template - static void fromJsonObject(json_object* object, std::vector& value) + static void fromJsonObject(json_object* object, std::vector& values) { checkType(object, json_type_array); int length = json_object_array_length(object); - value.resize(static_cast(length)); + values.resize(static_cast(length)); for (int i = 0; i < length; ++i) { - fromJsonObject(json_object_array_get_idx(object, i), value[static_cast(i)]); + fromJsonObject(json_object_array_get_idx(object, i), values[static_cast(i)]); + } + } + + template + static void fromJsonObject(json_object* object, std::array& values) + { + checkType(object, json_type_array); + + for (std::size_t i = 0; i < N; ++i) { + fromJsonObject(json_object_array_get_idx(object, i), values[i]); } } diff --git a/libs/config/from-kvjson-visitor.hpp b/libs/config/from-kvjson-visitor.hpp index cc6e14a..96f3888 100644 --- a/libs/config/from-kvjson-visitor.hpp +++ b/libs/config/from-kvjson-visitor.hpp @@ -167,6 +167,25 @@ private: } } + template + void getValue(const std::string& name, std::array& 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; + } + for (std::size_t i = 0; i < N; ++i) { + visitor.getValue(i, values[i]); + } + } + template::value, int>::type = 0> void getValue(int i, T& t) { @@ -213,6 +232,21 @@ private: } } + template + void getValue(int i, std::array& values) + { + std::string k = key(mKeyPrefix, std::to_string(i)); + + FromKVJsonVisitor visitor(*this, i, false); + if (mStore.exists(k)) { + json_object_put(visitor.mObject); + visitor.mObject = nullptr; + } + for (std::size_t idx = 0; idx < N; ++idx) { + visitor.getValue(idx, values[idx]); + } + } + static void checkType(json_object* object, json_type type) { if (type != json_object_get_type(object)) { diff --git a/libs/config/kvstore.hpp b/libs/config/kvstore.hpp index 583d534..fe30e21 100644 --- a/libs/config/kvstore.hpp +++ b/libs/config/kvstore.hpp @@ -141,6 +141,8 @@ private: void setInternal(const std::string& key, const T& value); template void setInternal(const std::string& key, const std::vector& values); + template + void setInternal(const std::string& key, const std::array& values); std::string getInternal(const std::string& key, std::string*); char* getInternal(const std::string& key, char**); @@ -149,6 +151,8 @@ private: T getInternal(const std::string& key, T*); template std::vector getInternal(const std::string& key, std::vector*); + template + std::array getInternal(const std::string& key, std::array*); std::string mPath; sqlite3::Connection mConn; @@ -204,12 +208,39 @@ void KVStore::setInternal(const std::string& key, const std::vector& values) setInternal(key, strValues); } +template +void KVStore::setInternal(const std::string& key, const std::array& values) +{ + std::vector strValues(N); + + std::transform(values.begin(), + values.end(), + strValues.begin(), + toString); + + setInternal(key, strValues); +} + template T KVStore::getInternal(const std::string& key, T*) { return fromString(getInternal(key, static_cast(nullptr))); } +template +std::array KVStore::getInternal(const std::string& key, std::array*) +{ + std::vector strValues = getInternal(key, static_cast*>(nullptr)); + std::array values; + + std::transform(strValues.begin(), + strValues.end(), + values.begin(), + fromString); + + return values; +} + template std::vector KVStore::getInternal(const std::string& key, std::vector*) { diff --git a/libs/config/to-fdstore-visitor.hpp b/libs/config/to-fdstore-visitor.hpp index 7da3d76..7ce8058 100644 --- a/libs/config/to-fdstore-visitor.hpp +++ b/libs/config/to-fdstore-visitor.hpp @@ -29,8 +29,10 @@ #include "config/fdstore.hpp" #include "config/types.hpp" -#include +#include #include +#include +#include namespace config { @@ -90,7 +92,15 @@ private: void writeInternal(const std::vector& values) { writeInternal(values.size()); - for (const T& value : values) { + for (const T& value: values) { + writeInternal(value); + } + } + + template + void writeInternal(const std::array& values) + { + for (const T& value: values) { writeInternal(value); } } diff --git a/libs/config/to-gvariant-visitor.hpp b/libs/config/to-gvariant-visitor.hpp index 40c002d..1bd1375 100644 --- a/libs/config/to-gvariant-visitor.hpp +++ b/libs/config/to-gvariant-visitor.hpp @@ -29,6 +29,7 @@ #include "config/is-union.hpp" #include "config/types.hpp" +#include #include #include #include @@ -100,6 +101,20 @@ private: } } + template + void writeInternal(const std::array& values) + { + if (!values.empty()) { + g_variant_builder_open(mBuilder, G_VARIANT_TYPE_ARRAY); + for (const T& v : values) { + writeInternal(v); + } + g_variant_builder_close(mBuilder); + } else { + g_variant_builder_add(mBuilder, "as", NULL); + } + } + 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 717b952..3c746d2 100644 --- a/libs/config/to-json-visitor.hpp +++ b/libs/config/to-json-visitor.hpp @@ -135,6 +135,16 @@ private: return array; } + template + static json_object* toJsonObject(const std::array& values) + { + json_object* array = json_object_new_array(); + for (const T& v : values) { + json_object_array_add(array, toJsonObject(v)); + } + return array; + } + template::value>::type> static json_object* toJsonObject(const T& value) { diff --git a/tests/unit_tests/config/testconfig-example.hpp b/tests/unit_tests/config/testconfig-example.hpp index 949239d..c6f8476 100644 --- a/tests/unit_tests/config/testconfig-example.hpp +++ b/tests/unit_tests/config/testconfig-example.hpp @@ -97,6 +97,8 @@ struct TestConfig { std::vector stringVector; std::vector doubleVector; + std::array intArray; + SubConfig subObj; std::vector subVector; @@ -120,6 +122,8 @@ struct TestConfig { stringVector, doubleVector, + intArray, + subObj, subVector, @@ -159,6 +163,7 @@ const std::string jsonTestString = "\"intVector\": [ 1, 2, 3 ], " "\"stringVector\": [ \"a\", \"b\" ], " "\"doubleVector\": [ 0.000000, 1.000000, 2.000000 ], " + "\"intArray\": [ 0, 1 ], " "\"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 } } ], " @@ -182,6 +187,7 @@ const std::string jsonEmptyTestString = "\"intVector\": [ ], " "\"stringVector\": [ ], " "\"doubleVector\": [ ], " + "\"intArray\": [ ], " "\"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 68311eb..a134b7b 100644 --- a/tests/unit_tests/config/ut-configuration.cpp +++ b/tests/unit_tests/config/ut-configuration.cpp @@ -71,9 +71,9 @@ BOOST_AUTO_TEST_CASE(FromJsonString) BOOST_REQUIRE_EQUAL(0, testConfig.emptyIntVector.size()); BOOST_REQUIRE_EQUAL(3, testConfig.intVector.size()); - BOOST_CHECK_EQUAL(1, testConfig.intVector[0]); - BOOST_CHECK_EQUAL(2, testConfig.intVector[1]); - BOOST_CHECK_EQUAL(3, testConfig.intVector[2]); + BOOST_CHECK_EQUAL(1, testConfig.intVector[0]); + BOOST_CHECK_EQUAL(2, testConfig.intVector[1]); + BOOST_CHECK_EQUAL(3, testConfig.intVector[2]); BOOST_REQUIRE_EQUAL(2, testConfig.stringVector.size()); BOOST_CHECK_EQUAL("a", testConfig.stringVector[0]); @@ -84,6 +84,10 @@ BOOST_AUTO_TEST_CASE(FromJsonString) BOOST_CHECK_CLOSE(1.0, testConfig.doubleVector[1], TOLERANCE); BOOST_CHECK_CLOSE(2.0, testConfig.doubleVector[2], TOLERANCE); + BOOST_REQUIRE_EQUAL(2, testConfig.intArray.size()); + BOOST_CHECK_EQUAL(0, testConfig.intArray[0]); + BOOST_CHECK_EQUAL(1, testConfig.intArray[1]); + 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 59f52b01c6034c5895893266873b32d9761d7381 Mon Sep 17 00:00:00 2001 From: Krzysztof Dynowski Date: Mon, 5 Oct 2015 11:01:14 +0200 Subject: [PATCH 04/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 05/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 06/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 07/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 08/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 09/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 10/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 11/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 12/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 13/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 14/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 15/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 16/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