From f339ca9f0d3f53404f91fd2aeb1a79f375b6b58f Mon Sep 17 00:00:00 2001 From: "sangwan.kwon" Date: Mon, 4 Sep 2017 05:18:50 -0400 Subject: [PATCH] Add GDBus introspection class Change-Id: Icdf25b14552cbf37e414596e54863cde7d7d5d15 Signed-off-by: sangwan.kwon --- include/klay/dbus/introspection.h | 74 ++++++++++++ src/CMakeLists.txt | 1 + src/dbus/introspection.cpp | 187 ++++++++++++++++++++++++++++++ test/dbus.cpp | 180 +++++++++++++++++++++++++++- 4 files changed, 441 insertions(+), 1 deletion(-) create mode 100644 include/klay/dbus/introspection.h create mode 100644 src/dbus/introspection.cpp diff --git a/include/klay/dbus/introspection.h b/include/klay/dbus/introspection.h new file mode 100644 index 0000000..5960876 --- /dev/null +++ b/include/klay/dbus/introspection.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 + */ + +#ifndef __RUNTIME_DBUS_INTROSPECTION_H__ +#define __RUNTIME_DBUS_INTROSPECTION_H__ + +#include + +#include + +namespace dbus { + +using BusNode = GDBusNodeInfo*; +using Interface = GDBusInterfaceInfo*; +using Method = GDBusMethodInfo*; +using Signal = GDBusSignalInfo*; +using Property = GDBusPropertyInfo*; + +class Introspection { +public: + explicit Introspection(const std::string &xmlData); + virtual ~Introspection(void); + + Introspection(const Introspection &) = delete; + Introspection(Introspection &&) = delete; + Introspection &operator=(const Introspection &) = delete; + Introspection &operator=(Introspection &&) = delete; + + Interface getInterface(const std::string &name) const; + Method getMethod(const std::string &interfaceName, + const std::string &methodName) const; + Signal getSignal(const std::string &interfaceName, + const std::string &signalName) const; + Property getProperty(const std::string &interfaceName, + const std::string &propertyName) const; + + std::string getXmlData(unsigned int indent = 0); + + void addInterface(const std::string &name); + void addMethod(const std::string &interfaceName, const std::string &methodData); + void addSignal(const std::string &interfaceName, const std::string &signalData); + void addProperty(const std::string &interfaceName, const std::string &propertyData); + +private: + BusNode getBusNode(const std::string &xmlData); + void update(void); + + std::string getInterfaceBeginTag(const std::string &name) const; + std::string getInterfaceEndTag(void) const; + + void addInternalData(const std::string &interfaceName, const std::string &data); + + void checkDataFormat(const std::string &data) const; + + BusNode busNode; + std::string xmlData; +}; + +} // namespace dbus + +#endif //! __RUNTIME_DBUS_INTROSPECTION_H__ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f4d6828..6073882 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -46,6 +46,7 @@ SET (KLAY_SOURCES ${KLAY_SRC}/error.cpp ${KLAY_SRC}/dbus/error.cpp ${KLAY_SRC}/dbus/variant.cpp ${KLAY_SRC}/dbus/connection.cpp + ${KLAY_SRC}/dbus/introspection.cpp ${KLAY_SRC}/audit/logger.cpp ${KLAY_SRC}/audit/logger-core.cpp ${KLAY_SRC}/audit/null-sink.cpp diff --git a/src/dbus/introspection.cpp b/src/dbus/introspection.cpp new file mode 100644 index 0000000..182fe13 --- /dev/null +++ b/src/dbus/introspection.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved + * + * 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 + */ + +#include +#include +#include + +#include +#include + +namespace dbus { + +Introspection::Introspection(const std::string &xmlData) : + busNode(getBusNode(xmlData)), xmlData(getXmlData()) +{ +} + +Introspection::~Introspection(void) +{ + if (busNode) + ::g_dbus_node_info_unref(busNode); +} + +BusNode Introspection::getBusNode(const std::string &xmlData) +{ + if (xmlData.empty()) + throw runtime::Exception("Invalid argument."); + + dbus::Error error; + auto busNode = ::g_dbus_node_info_new_for_xml(xmlData.c_str(), &error); + if (busNode == nullptr || error) + throw runtime::Exception("Failed to get BusNode."); + + return busNode; +} + +Interface Introspection::getInterface(const std::string &name) const +{ + if (name.empty()) + throw runtime::Exception("Invalid argument."); + + return ::g_dbus_node_info_lookup_interface(busNode, name.c_str()); +} + +Method Introspection::getMethod(const std::string &interfaceName, + const std::string &methodName) const +{ + auto interface = getInterface(interfaceName); + if (interface == nullptr || methodName.empty()) + throw runtime::Exception("Invalid argument."); + + return ::g_dbus_interface_info_lookup_method(interface, methodName.c_str()); +} + +Signal Introspection::getSignal(const std::string &interfaceName, + const std::string &signalName) const +{ + auto interface = getInterface(interfaceName); + if (interface == nullptr || signalName.empty()) + throw runtime::Exception("Invalid argument."); + + return ::g_dbus_interface_info_lookup_signal(interface, signalName.c_str()); +} + +Property Introspection::getProperty(const std::string &interfaceName, + const std::string &propertyName) const +{ + auto interface = getInterface(interfaceName); + if (interface == nullptr || propertyName.empty()) + throw runtime::Exception("Invalid argument."); + + return ::g_dbus_interface_info_lookup_property(interface, propertyName.c_str()); +} + +std::string Introspection::getXmlData(unsigned int indent) +{ + using ScopedGString = std::unique_ptr>; + ScopedGString buf(g_string_new(""), [](GString *ptr) + { + ::g_string_free(ptr, TRUE); + }); + if (buf == nullptr) + throw runtime::Exception("Out of memory."); + + ::g_dbus_node_info_generate_xml(busNode, indent, buf.get()); + return std::string(buf->str); +} + +void Introspection::addInterface(const std::string &name) +{ + if (getInterface(name) != nullptr) + throw runtime::Exception("Interface is already exist:" + name); + + std::size_t offset = xmlData.find(""); + if (offset == std::string::npos) + throw runtime::Exception("Failed to find ."); + + xmlData.insert(offset, getInterfaceBeginTag(name) + getInterfaceEndTag()); + update(); +} + +std::string Introspection::getInterfaceBeginTag(const std::string &name) const +{ + const std::string token = "@NAME@"; + std::string iTemplate = ""; + + iTemplate.replace(iTemplate.find(token), token.length(), name); + + return iTemplate; +} + +std::string Introspection::getInterfaceEndTag(void) const +{ + return ""; +} + +// TODO: Check more strict. +void Introspection::checkDataFormat(const std::string &data) const +{ + if (data.empty() || data.length() < 3) + throw runtime::Exception("Invalid argument."); + + const std::string beginChar = "<"; + if (data.compare(0, beginChar.length(), beginChar) != 0) + throw runtime::Exception("Xml data should be begin as: " + beginChar); + + const std::string endChar = "/>"; + if (data.compare(data.length() - endChar.length(), endChar.length(), endChar) != 0 && + data.find("/") == std::string::npos) + throw runtime::Exception("Xml data should be contain '/' or end as: " + endChar); +} + +void Introspection::addMethod(const std::string &interfaceName, + const std::string &methodData) +{ + addInternalData(interfaceName, methodData); +} + +void Introspection::addSignal(const std::string &interfaceName, + const std::string &signalData) +{ + addInternalData(interfaceName, signalData); +} + +void Introspection::addProperty(const std::string &interfaceName, + const std::string &propertyData) +{ + addInternalData(interfaceName, propertyData); +} + +void Introspection::addInternalData(const std::string &interfaceName, + const std::string &data) +{ + checkDataFormat(data); + + std::string iTemplate = getInterfaceBeginTag(interfaceName); + std::size_t offset = xmlData.find(iTemplate); + if (offset == std::string::npos) + throw runtime::Exception("Failed to find interface xml node: " + interfaceName); + + xmlData.insert(offset + iTemplate.length() + 1, data); + update(); +} + +void Introspection::update(void) +{ + if (busNode) + ::g_dbus_node_info_unref(busNode); + + busNode = getBusNode(xmlData); + xmlData = getXmlData(); +} + +} // namespace dbus diff --git a/test/dbus.cpp b/test/dbus.cpp index 3cf2213..a10f309 100644 --- a/test/dbus.cpp +++ b/test/dbus.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -33,6 +34,27 @@ const std::string TESTSVC_METHOD_NOOP = "Noop"; const std::string TESTSVC_METHOD_PROCESS = "Process"; const std::string TESTSVC_METHOD_THROW = "Throw"; const std::string TESTSVC_SIGNAL_NOTIFY = "Notify"; +const std::string TESTSVC_NOT_EXIST = "None"; + +const std::string TESTSVC_INTERFACE_NEW_NAME = "NewInterface"; +const std::string TESTSVC_METHOD_NEW_NAME = "NewMethod"; +const std::string TESTSVC_SIGNAL_NEW_NAME = "NewSignal"; +const std::string TESTSVC_PROPERTY_NEW_NAME = "NewProperty"; + +const std::string TESTSVC_METHOD_NEW_DATA = + "" + " " + " " + ""; +const std::string TESTSVC_SIGNAL_NEW_DATA = + "" + " " + ""; +const std::string TESTSVC_PROPERTY_NEW_DATA = + ""; + +const std::string TESTSVC_WRONG_DATA_TYPE1 = "signal'/>"; +const std::string TESTSVC_WRONG_DATA_TYPE2 = ""; const std::string manifest = "" @@ -46,7 +68,7 @@ const std::string manifest = " " " " " " - " " + " " " " " " ""; @@ -147,3 +169,159 @@ TESTCASE(DbusRegisterObjectTest) ERROR(KSINK, e.what()); } } + +TESTCASE(DBusIntrospectionGetterTest) +{ + try { + dbus::Introspection is(manifest); + + dbus::Interface interface = is.getInterface(TESTSVC_NOT_EXIST); + TEST_EXPECT(true, interface == nullptr); + + interface = is.getInterface(TESTSVC_INTERFACE); + TEST_EXPECT(true, interface != nullptr); + + dbus::Method method = is.getMethod(TESTSVC_INTERFACE, TESTSVC_NOT_EXIST); + TEST_EXPECT(true, method == nullptr); + + method = is.getMethod(TESTSVC_INTERFACE, TESTSVC_METHOD_THROW); + TEST_EXPECT(true, method != nullptr); + + dbus::Signal signal = is.getSignal(TESTSVC_INTERFACE, TESTSVC_NOT_EXIST); + TEST_EXPECT(true, signal == nullptr); + + signal = is.getSignal(TESTSVC_INTERFACE, TESTSVC_SIGNAL_NOTIFY); + TEST_EXPECT(true, signal != nullptr); + + signal = is.getSignal(TESTSVC_INTERFACE, TESTSVC_SIGNAL_NEW_NAME); + TEST_EXPECT(true, signal == nullptr); + + } catch (std::exception& e) { + ERROR(KSINK, e.what()); + TEST_EXPECT(true, false); + } +} + +TESTCASE(DBusIntrospectionDuplicatedInterface) +{ + try { + dbus::Introspection is(manifest); + is.addInterface(TESTSVC_INTERFACE); + TEST_EXPECT(true, false); + } catch (std::exception& e) { + TEST_EXPECT(true, true); + } +} + +TESTCASE(DBusIntrospectionAddInterfaceAndMethod) +{ + try { + dbus::Introspection is(manifest); + dbus::Interface interface = is.getInterface(TESTSVC_INTERFACE_NEW_NAME); + TEST_EXPECT(true, interface == nullptr); + + is.addInterface(TESTSVC_INTERFACE_NEW_NAME); + + interface = is.getInterface(TESTSVC_INTERFACE_NEW_NAME); + TEST_EXPECT(true, interface != nullptr); + + dbus::Method method = is.getMethod(TESTSVC_INTERFACE_NEW_NAME, TESTSVC_METHOD_NEW_NAME); + TEST_EXPECT(true, method == nullptr); + + is.addMethod(TESTSVC_INTERFACE_NEW_NAME, TESTSVC_METHOD_NEW_DATA); + + method = is.getMethod(TESTSVC_INTERFACE_NEW_NAME, TESTSVC_METHOD_NEW_NAME); + TEST_EXPECT(true, method != nullptr); + + } catch (std::exception& e) { + ERROR(KSINK, e.what()); + TEST_EXPECT(true, false); + } +} + +TESTCASE(DBusIntrospectionAddMethod) +{ + try { + dbus::Introspection is(manifest); + dbus::Method method = is.getMethod(TESTSVC_INTERFACE, TESTSVC_METHOD_NEW_NAME); + TEST_EXPECT(true, method == nullptr); + + is.addMethod(TESTSVC_INTERFACE, TESTSVC_METHOD_NEW_DATA); + + method = is.getMethod(TESTSVC_INTERFACE, TESTSVC_METHOD_NEW_NAME); + TEST_EXPECT(true, method != nullptr); + } catch (std::exception& e) { + ERROR(KSINK, e.what()); + TEST_EXPECT(true, false); + } +} + +TESTCASE(DBusIntrospectionAddSignal) +{ + try { + dbus::Introspection is(manifest); + dbus::Signal signal = is.getSignal(TESTSVC_INTERFACE, TESTSVC_SIGNAL_NEW_NAME); + TEST_EXPECT(true, signal == nullptr); + + is.addSignal(TESTSVC_INTERFACE, TESTSVC_SIGNAL_NEW_DATA); + + signal = is.getSignal(TESTSVC_INTERFACE, TESTSVC_SIGNAL_NEW_NAME); + TEST_EXPECT(true, signal != nullptr); + } catch (std::exception& e) { + ERROR(KSINK, e.what()); + TEST_EXPECT(true, false); + } +} + +TESTCASE(DBusIntrospectionAddProperty) +{ + try { + dbus::Introspection is(manifest); + dbus::Property property = is.getProperty(TESTSVC_INTERFACE, TESTSVC_PROPERTY_NEW_NAME); + TEST_EXPECT(true, property == nullptr); + + is.addProperty(TESTSVC_INTERFACE, TESTSVC_PROPERTY_NEW_DATA); + + property = is.getProperty(TESTSVC_INTERFACE, TESTSVC_PROPERTY_NEW_NAME); + TEST_EXPECT(true, property != nullptr); + } catch (std::exception& e) { + ERROR(KSINK, e.what()); + TEST_EXPECT(true, false); + } +} + +TESTCASE(DBusIntrospectionAddTotal) +{ + try { + dbus::Introspection is(manifest); + is.addMethod(TESTSVC_INTERFACE, TESTSVC_METHOD_NEW_DATA); + is.addSignal(TESTSVC_INTERFACE, TESTSVC_SIGNAL_NEW_DATA); + is.addProperty(TESTSVC_INTERFACE, TESTSVC_PROPERTY_NEW_DATA); + + is.addInterface(TESTSVC_INTERFACE_NEW_NAME); + is.addMethod(TESTSVC_INTERFACE_NEW_NAME, TESTSVC_METHOD_NEW_DATA); + is.addSignal(TESTSVC_INTERFACE_NEW_NAME, TESTSVC_SIGNAL_NEW_DATA); + is.addProperty(TESTSVC_INTERFACE_NEW_NAME, TESTSVC_PROPERTY_NEW_DATA); + } catch (std::exception& e) { + ERROR(KSINK, e.what()); + TEST_EXPECT(true, false); + } +} + +TESTCASE(DBusIntrospectionCheckDataFormat) +{ + try { + dbus::Introspection is(manifest); + is.addMethod(TESTSVC_INTERFACE, TESTSVC_WRONG_DATA_TYPE1); + TEST_EXPECT(true, false); + } catch (std::exception& e) { + TEST_EXPECT(true, true); + } + try { + dbus::Introspection is(manifest); + is.addMethod(TESTSVC_INTERFACE, TESTSVC_WRONG_DATA_TYPE2); + TEST_EXPECT(true, false); + } catch (std::exception& e) { + TEST_EXPECT(true, true); + } +} -- 2.34.1