--- /dev/null
+/*
+ * 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 <gio/gio.h>
+
+#include <string>
+
+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__
${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
--- /dev/null
+/*
+ * 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 <klay/dbus/introspection.h>
+#include <klay/dbus/error.h>
+#include <klay/exception.h>
+
+#include <memory>
+#include <functional>
+
+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<GString, std::function<void(GString*)>>;
+ 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("</node>");
+ if (offset == std::string::npos)
+ throw runtime::Exception("Failed to find </node>.");
+
+ xmlData.insert(offset, getInterfaceBeginTag(name) + getInterfaceEndTag());
+ update();
+}
+
+std::string Introspection::getInterfaceBeginTag(const std::string &name) const
+{
+ const std::string token = "@NAME@";
+ std::string iTemplate = "<interface name=\"@NAME@\">";
+
+ iTemplate.replace(iTemplate.find(token), token.length(), name);
+
+ return iTemplate;
+}
+
+std::string Introspection::getInterfaceEndTag(void) const
+{
+ return "</interface>";
+}
+
+// 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
#include <klay/audit/logger.h>
#include <klay/dbus/variant.h>
#include <klay/dbus/connection.h>
+#include <klay/dbus/introspection.h>
#include <klay/latch.h>
#include <klay/testbench.h>
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 =
+ "<method name='" + TESTSVC_METHOD_NEW_NAME + "'>"
+ " <arg type='s' name='argument' direction='in'/>"
+ " <arg type='s' name='response' direction='out'/>"
+ "</method>";
+const std::string TESTSVC_SIGNAL_NEW_DATA =
+ "<signal name='" + TESTSVC_SIGNAL_NEW_NAME + "'>"
+ " <arg type='s' name='argument'/>"
+ "</signal>";
+const std::string TESTSVC_PROPERTY_NEW_DATA =
+ "<property name='" + TESTSVC_PROPERTY_NEW_NAME + "' type='y' access='readwrite'/>";
+
+const std::string TESTSVC_WRONG_DATA_TYPE1 = "signal'/>";
+const std::string TESTSVC_WRONG_DATA_TYPE2 = "<signal>";
const std::string manifest =
"<node>"
" <arg type='i' name='argument' direction='in'/>"
" </method>"
" <signal name='" + TESTSVC_SIGNAL_NOTIFY + "'>"
- " <arg type='s' name='arument'/>"
+ " <arg type='s' name='argument'/>"
" </signal>"
" </interface>"
"</node>";
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);
+ }
+}