From: sangwan.kwon Date: Mon, 25 Sep 2017 09:10:08 +0000 (-0400) Subject: Add high-level dbus signal class X-Git-Tag: submit/tizen/20171019.011823^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=b530df370e66651966cb15ede02c28a0f2c23589;p=platform%2Fcore%2Fsecurity%2Fklay.git Add high-level dbus signal class Change-Id: Ib7247c440a331607f57bac4ab795e2efe52861cd Signed-off-by: sangwan.kwon --- diff --git a/include/klay/dbus/connection.h b/include/klay/dbus/connection.h index 11cf298..1cbfff3 100644 --- a/include/klay/dbus/connection.h +++ b/include/klay/dbus/connection.h @@ -27,6 +27,8 @@ namespace dbus { +typedef std::function SignalCallback; + class Connection { public: typedef unsigned int NameId; @@ -35,7 +37,6 @@ public: typedef std::function VoidCallback; - typedef std::function SignalCallback; typedef std::function ClientVanishedCallback; diff --git a/include/klay/dbus/introspection.h b/include/klay/dbus/introspection.h index 5960876..711611e 100644 --- a/include/klay/dbus/introspection.h +++ b/include/klay/dbus/introspection.h @@ -20,6 +20,8 @@ #include #include +#include +#include namespace dbus { @@ -29,6 +31,8 @@ using Method = GDBusMethodInfo*; using Signal = GDBusSignalInfo*; using Property = GDBusPropertyInfo*; +using XmlProperties = std::vector>; + class Introspection { public: explicit Introspection(const std::string &xmlData); @@ -48,18 +52,27 @@ public: 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); diff --git a/include/klay/dbus/signal.h b/include/klay/dbus/signal.h new file mode 100644 index 0000000..b180918 --- /dev/null +++ b/include/klay/dbus/signal.h @@ -0,0 +1,86 @@ +/* + * 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 +#include + +#include + +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 + 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 +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(argumnets)...); +} + +} // namespace signal +} // namespace dbus + +#endif //! __RUNTIME_DBUS_SIGNAL_H__ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6073882..832c6ba 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -47,6 +47,7 @@ SET (KLAY_SOURCES ${KLAY_SRC}/error.cpp ${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 diff --git a/src/dbus/introspection.cpp b/src/dbus/introspection.cpp index 182fe13..4bc1a95 100644 --- a/src/dbus/introspection.cpp +++ b/src/dbus/introspection.cpp @@ -16,13 +16,27 @@ #include #include +#include #include +#include +#include + #include #include namespace dbus { +namespace { + +const std::string MANIFEST_TEMPLATE = ""; + +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()) { @@ -99,6 +113,32 @@ std::string Introspection::getXmlData(unsigned int indent) 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 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) @@ -108,23 +148,40 @@ void Introspection::addInterface(const std::string &name) if (offset == std::string::npos) throw runtime::Exception("Failed to find ."); - 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 = ""; + 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 ""; + return std::string(""); } // TODO: Check more strict. @@ -149,6 +206,33 @@ void Introspection::addMethod(const std::string &interfaceName, 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) { @@ -166,7 +250,10 @@ void Introspection::addInternalData(const std::string &interfaceName, { 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); diff --git a/src/dbus/signal.cpp b/src/dbus/signal.cpp new file mode 100644 index 0000000..2498ed8 --- /dev/null +++ b/src/dbus/signal.cpp @@ -0,0 +1,89 @@ +/* + * 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 + +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 diff --git a/test/dbus.cpp b/test/dbus.cpp index a10f309..f6516d6 100644 --- a/test/dbus.cpp +++ b/test/dbus.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -36,10 +37,11 @@ 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_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 = "" @@ -56,6 +58,8 @@ 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 TESTSVC_MANIFEST_PATH = TEST_DATA_DIR "/manifest"; + const std::string manifest = "" " " @@ -325,3 +329,61 @@ TESTCASE(DBusIntrospectionCheckDataFormat) 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); + } +}