namespace dbus {
+typedef std::function<void(Variant parameters)> SignalCallback;
+
class Connection {
public:
typedef unsigned int NameId;
typedef std::function<void()> VoidCallback;
- typedef std::function<void(Variant parameters)> SignalCallback;
typedef std::function<void(const std::string& name)> ClientVanishedCallback;
#include <gio/gio.h>
#include <string>
+#include <vector>
+#include <utility>
namespace dbus {
using Signal = GDBusSignalInfo*;
using Property = GDBusPropertyInfo*;
+using XmlProperties = std::vector<std::pair<std::string, std::string>>;
+
class Introspection {
public:
explicit Introspection(const std::string &xmlData);
const std::string &propertyName) const;
std::string getXmlData(unsigned int indent = 0);
+ static std::string createXmlDataFromFile(const std::string &path);
+ static void writeXmlDataToFile(const std::string &path, const std::string &xmlData);
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);
+ void addSignal(const std::string &interfaceName,
+ const std::string &signalName,
+ const std::string &argumentType);
+
private:
BusNode getBusNode(const std::string &xmlData);
void update(void);
- std::string getInterfaceBeginTag(const std::string &name) const;
- std::string getInterfaceEndTag(void) const;
+ std::string getXmlBeginTag(const std::string &node,
+ const XmlProperties &properties) const;
+ std::string getXmlEndTag(const std::string &node) const;
+
+ std::string parseXmlProperties(const XmlProperties &properties) const;
void addInternalData(const std::string &interfaceName, const std::string &data);
--- /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_SIGNAL_H__
+#define __RUNTIME_DBUS_SIGNAL_H__
+
+#include <string>
+#include <utility>
+
+#include <klay/dbus/connection.h>
+
+namespace dbus {
+namespace signal {
+
+class Sender {
+public:
+ explicit Sender(const std::string &objectPath, const std::string &interfaceName);
+ virtual ~Sender(void) = default;
+
+ void setBusName(const std::string &name);
+ void setInterfaceName(const std::string &name);
+
+ void addSignal(const std::string &manifestPath,
+ const std::string &signalName,
+ const std::string &argumentType) const;
+
+ template<typename... Arguments>
+ void emit(const std::string &signalName,
+ const std::string &argumentType,
+ Arguments&&... argumnets) const;
+
+private:
+ std::string busName;
+ std::string objectPath;
+ std::string interfaceName;
+};
+
+class Receiver {
+public:
+ explicit Receiver(const std::string &objectPath, const std::string &interfaceName);
+ virtual ~Receiver(void) = default;
+
+ void setInterfaceName(const std::string &name);
+ void setSenderName(const std::string &name);
+
+ unsigned int subscribe(const std::string &signalName,
+ const SignalCallback& signalCallback) const;
+ void unsubscribe(unsigned int id) const;
+
+private:
+ std::string objectPath;
+ std::string interfaceName;
+ std::string senderName;
+};
+
+template<typename... Arguments>
+void Sender::emit(const std::string &signalName,
+ const std::string &argumentType,
+ Arguments&&... argumnets) const
+{
+ dbus::Connection &conn = dbus::Connection::getSystem();
+ conn.emitSignal(busName,
+ objectPath,
+ interfaceName,
+ signalName,
+ argumentType,
+ std::forward<Arguments>(argumnets)...);
+}
+
+} // namespace signal
+} // namespace dbus
+
+#endif //! __RUNTIME_DBUS_SIGNAL_H__
${KLAY_SRC}/dbus/variant.cpp
${KLAY_SRC}/dbus/connection.cpp
${KLAY_SRC}/dbus/introspection.cpp
+ ${KLAY_SRC}/dbus/signal.cpp
${KLAY_SRC}/audit/logger.cpp
${KLAY_SRC}/audit/logger-core.cpp
${KLAY_SRC}/audit/null-sink.cpp
#include <klay/dbus/introspection.h>
#include <klay/dbus/error.h>
+#include <klay/filesystem.h>
#include <klay/exception.h>
+#include <unistd.h>
+#include <fcntl.h>
+
#include <memory>
#include <functional>
namespace dbus {
+namespace {
+
+const std::string MANIFEST_TEMPLATE = "<node></node>";
+
+const std::string INTERFACE_NODE = "interface";
+const std::string SIGNAL_NODE = "signal";
+const std::string ARGUMENT_NODE = "arg";
+
+}
+
Introspection::Introspection(const std::string &xmlData) :
busNode(getBusNode(xmlData)), xmlData(getXmlData())
{
return std::string(buf->str);
}
+std::string Introspection::createXmlDataFromFile(const std::string &path)
+{
+ runtime::File manifest(path);
+ if (!manifest.exists()) {
+ manifest.create(644);
+ manifest.lock();
+ manifest.write(MANIFEST_TEMPLATE.c_str(), MANIFEST_TEMPLATE.length());
+ manifest.unlock();
+ }
+
+ manifest.open(O_RDONLY);
+ std::vector<char> buf(manifest.size() + 1);
+ manifest.read(buf.data(), manifest.size());
+ return std::string(buf.data());
+}
+
+void Introspection::writeXmlDataToFile(const std::string &path,
+ const std::string &xmlData)
+{
+ runtime::File manifest(path);
+ manifest.open(O_WRONLY | O_TRUNC);
+ manifest.lock();
+ manifest.write(xmlData.c_str(), xmlData.length());
+ manifest.unlock();
+}
+
void Introspection::addInterface(const std::string &name)
{
if (getInterface(name) != nullptr)
if (offset == std::string::npos)
throw runtime::Exception("Failed to find </node>.");
- xmlData.insert(offset, getInterfaceBeginTag(name) + getInterfaceEndTag());
+ XmlProperties properties;
+ properties.emplace_back(std::make_pair("name", name));
+
+ xmlData.insert(offset, getXmlBeginTag(INTERFACE_NODE, properties) +
+ getXmlEndTag(INTERFACE_NODE));
update();
}
-std::string Introspection::getInterfaceBeginTag(const std::string &name) const
+std::string Introspection::parseXmlProperties(const XmlProperties &properties) const
{
- const std::string token = "@NAME@";
- std::string iTemplate = "<interface name=\"@NAME@\">";
+ std::string parsed = "";
+ for (const auto &property : properties)
+ parsed.append(property.first + "=\"" + property.second + "\" ");
- iTemplate.replace(iTemplate.find(token), token.length(), name);
+ return parsed.substr(0, parsed.length() - 1);
+};
- return iTemplate;
+std::string Introspection::getXmlBeginTag(const std::string &node,
+ const XmlProperties &properties) const
+{
+ const std::string nameToken = "@NODE@";
+ const std::string propertyToken = "@PROPERTIES@";
+ std::string tagTemplate = "<@NODE@ @PROPERTIES@>";
+
+ std::string parsed = parseXmlProperties(properties);
+ tagTemplate.replace(tagTemplate.find(nameToken), nameToken.length(), node);
+ tagTemplate.replace(tagTemplate.find(propertyToken), propertyToken.length(), parsed);
+
+ return tagTemplate;
}
-std::string Introspection::getInterfaceEndTag(void) const
+std::string Introspection::getXmlEndTag(const std::string &node) const
{
- return "</interface>";
+ return std::string("</" + node + ">");
}
// TODO: Check more strict.
addInternalData(interfaceName, methodData);
}
+void Introspection::addSignal(const std::string &interfaceName,
+ const std::string &signalName,
+ const std::string &argumentType)
+{
+ XmlProperties properties;
+ properties.emplace_back(std::make_pair("name", signalName));
+ std::string xmlData = getXmlBeginTag(SIGNAL_NODE, properties);
+
+ int index = 1;
+ for (const auto &type : argumentType) {
+ if (type == '(' || type == ')')
+ continue;
+
+ properties.clear();
+ properties.emplace_back(std::make_pair("type", std::string(1, type)));
+ properties.emplace_back(std::make_pair("name",
+ "argument" + std::to_string(index++)));
+
+ xmlData.append(getXmlBeginTag(ARGUMENT_NODE, properties));
+ xmlData.append(getXmlEndTag(ARGUMENT_NODE));
+ }
+
+ xmlData.append(getXmlEndTag(SIGNAL_NODE));
+
+ addInternalData(interfaceName, xmlData);
+}
+
void Introspection::addSignal(const std::string &interfaceName,
const std::string &signalData)
{
{
checkDataFormat(data);
- std::string iTemplate = getInterfaceBeginTag(interfaceName);
+ XmlProperties properties;
+ properties.emplace_back(std::make_pair("name", interfaceName));
+
+ std::string iTemplate = getXmlBeginTag(INTERFACE_NODE, properties);
std::size_t offset = xmlData.find(iTemplate);
if (offset == std::string::npos)
throw runtime::Exception("Failed to find interface xml node: " + interfaceName);
--- /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/signal.h>
+#include <klay/dbus/introspection.h>
+#include <klay/exception.h>
+
+namespace dbus {
+namespace signal {
+
+Sender::Sender(const std::string &objectPath, const std::string &interfaceName) :
+ busName(""), objectPath(objectPath), interfaceName(interfaceName)
+{
+}
+
+void Sender::addSignal(const std::string &manifestPath,
+ const std::string &signalName,
+ const std::string &argumentType) const
+{
+ std::string xmlData = Introspection::createXmlDataFromFile(manifestPath);
+ Introspection introspect(xmlData);
+ if (introspect.getInterface(interfaceName) == nullptr)
+ introspect.addInterface(interfaceName);
+
+ if (introspect.getSignal(interfaceName, signalName) == nullptr)
+ introspect.addSignal(interfaceName, signalName, argumentType);
+ else
+ return;
+
+ Introspection::writeXmlDataToFile(manifestPath, introspect.getXmlData());
+}
+
+void Sender::setBusName(const std::string &name)
+{
+ busName = name;
+}
+
+void Sender::setInterfaceName(const std::string &name)
+{
+ interfaceName = name;
+}
+
+Receiver::Receiver(const std::string &objectPath, const std::string &interfaceName) :
+ objectPath(objectPath), interfaceName(interfaceName), senderName("")
+{
+}
+
+void Receiver::setInterfaceName(const std::string &name)
+{
+ interfaceName = name;
+}
+
+void Receiver::setSenderName(const std::string &name)
+{
+ senderName = name;
+}
+
+unsigned int Receiver::subscribe(const std::string &signalName,
+ const SignalCallback& signalCallback) const
+{
+ dbus::Connection &conn = dbus::Connection::getSystem();
+ return conn.subscribeSignal(senderName,
+ objectPath,
+ interfaceName,
+ signalName,
+ signalCallback);
+}
+
+void Receiver::unsubscribe(unsigned int id) const
+{
+ dbus::Connection &conn = dbus::Connection::getSystem();
+ return conn.unsubscribeSignal(id);
+}
+
+} // namespace signal
+} // namespace dbus
#include <klay/exception.h>
#include <klay/audit/logger.h>
#include <klay/dbus/variant.h>
+#include <klay/dbus/signal.h>
#include <klay/dbus/connection.h>
#include <klay/dbus/introspection.h>
#include <klay/latch.h>
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_RUNTIME_OBJECT_PATH = "/org/tizen/klay/runtime";
+const std::string TESTSVC_INTERFACE_NEW_NAME = "interface.api";
+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 + "'>"
const std::string TESTSVC_WRONG_DATA_TYPE1 = "signal'/>";
const std::string TESTSVC_WRONG_DATA_TYPE2 = "<signal>";
+const std::string TESTSVC_MANIFEST_PATH = TEST_DATA_DIR "/manifest";
+
const std::string manifest =
"<node>"
" <interface name='" + TESTSVC_INTERFACE + "'>"
TEST_EXPECT(true, true);
}
}
+
+TESTCASE(DBusSignalAddToNotExistManifest)
+{
+ try {
+ dbus::signal::Sender sender(TESTSVC_OBJECT_PATH, TESTSVC_INTERFACE_NEW_NAME);
+ sender.addSignal(TESTSVC_MANIFEST_PATH, TESTSVC_SIGNAL_NEW_NAME, "(ss)");
+ TEST_EXPECT(true, true);
+ } catch (std::exception& e) {
+ ERROR(KSINK, e.what());
+ TEST_EXPECT(true, false);
+ }
+}
+
+TESTCASE(DBusSignalAddToExistManifest)
+{
+ try {
+ dbus::signal::Sender sender(TESTSVC_OBJECT_PATH, TESTSVC_INTERFACE_NEW_NAME);
+ sender.addSignal(TESTSVC_MANIFEST_PATH, TESTSVC_SIGNAL_NEW_NAME, "(ss)");
+ TEST_EXPECT(true, true);
+ } catch (std::exception& e) {
+ ERROR(KSINK, e.what());
+ TEST_EXPECT(true, false);
+ }
+}
+
+TESTCASE(DBusSignalEmitTest)
+{
+ ScopedGMainLoop mainloop;
+
+ try {
+ std::string manifest = dbus::Introspection::createXmlDataFromFile(TESTSVC_MANIFEST_PATH);
+ dbus::Connection& svc = dbus::Connection::getSystem();
+ svc.registerObject(TESTSVC_RUNTIME_OBJECT_PATH, manifest, nullptr, nullptr);
+
+ std::string arg1 = "arg1";
+ std::string arg2 = "arg2";
+ auto onSignal = [&](dbus::Variant variant)
+ {
+ char *ret1 = NULL;
+ char *ret2 = NULL;
+ variant.get("(ss)", &ret1, &ret2);
+
+ TEST_EXPECT(true, arg1.compare(ret1) == 0);
+ TEST_EXPECT(true, arg2.compare(ret2) == 0);
+ };
+
+ dbus::signal::Receiver receiver(TESTSVC_RUNTIME_OBJECT_PATH, TESTSVC_INTERFACE_NEW_NAME);
+ receiver.subscribe(TESTSVC_SIGNAL_NEW_NAME, onSignal);
+
+ dbus::signal::Sender sender(TESTSVC_RUNTIME_OBJECT_PATH, TESTSVC_INTERFACE_NEW_NAME);
+ sender.emit(TESTSVC_SIGNAL_NEW_NAME, "(ss)", arg1.c_str(), arg2.c_str());
+
+ TEST_EXPECT(true, true);
+ } catch (std::exception& e) {
+ ERROR(KSINK, e.what());
+ TEST_EXPECT(true, false);
+ }
+}