2 Copyright (C) 2012 Intel Corporation
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with this library; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 #include "abstractdbusinterface.h"
21 #include <abstractroutingengine.h>
23 #include <boost/algorithm/string.hpp>
24 #include <boost/algorithm/string/predicate.hpp>
26 #include <listplusplus.h>
28 #include "varianttype.h"
29 #include "dbussignaller.h"
31 static DBusSignaller* signaller = nullptr;
33 unordered_map<string, AbstractDBusInterface*> AbstractDBusInterface::objectMap;
34 PropertyList AbstractDBusInterface::mimplementedProperties;
36 const uint getPid(const char *owner)
38 GError* error = nullptr;
39 GDBusProxy* dbus = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL,
40 "org.freedesktop.DBus",
42 "org.freedesktop.DBus",
48 throw std::runtime_error(error->message);
53 GVariant* pid = g_dbus_proxy_call_sync(dbus, "GetConnectionUnixProcessID", g_variant_new("(s)", owner), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
57 DebugOut(DebugOut::Error)<< error->message << endl;
62 g_variant_get(pid,"(u)",&thePid);
67 void AbstractDBusInterface::handleMyMethodCall(GDBusConnection *connection,
69 const gchar *object_path,
70 const gchar *interface_name,
71 const gchar *method_name,
73 GDBusMethodInvocation *invocation,
77 std::string method = method_name;
78 AbstractDBusInterface* iface = static_cast<AbstractDBusInterface*>(user_data);
80 if(DebugOut::getDebugThreshhold() >= 6)
82 DebugOut(6)<<"DBus method call from: "<<sender<< " pid: " <<getPid(sender)<< " interface: "<<interface_name<<" method: "<<method<<endl;
83 DebugOut(6)<<"DBus method call path: "<<object_path<<endl;
88 if(std::string(interface_name) == "org.freedesktop.DBus.Properties")
92 gchar* propertyName = nullptr;
93 gchar* ifaceName = nullptr;
94 g_variant_get(parameters, "(ss)", &ifaceName, &propertyName);
96 DebugOut(6) << "Parameter signature: " << g_variant_get_type_string(parameters) << endl;
97 // DebugOut(6) << "Get property " << propertyName << " for interface " << ifaceName << endl;
99 GError* error = nullptr;
100 auto value = AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, propertyName, &error, iface);
101 amb::make_super(error);
105 g_dbus_method_invocation_return_dbus_error(invocation, std::string(std::string(ifaceName)+".PropertyNotFound").c_str(), "Property not found in interface");
108 g_dbus_method_invocation_return_value(invocation, g_variant_new("(v)", value));
111 else if(method == "GetAll")
113 gchar* ifaceName = nullptr;
114 g_variant_get(parameters, "(s)", &ifaceName);
116 GVariantBuilder builder;
117 g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
119 auto propertyMap = iface->getProperties();
121 for(auto itr : propertyMap)
123 auto prop = itr.second;
124 GError* error = nullptr;
125 auto value = AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, prop->name().c_str(), &error, iface);
126 amb::make_super(error);
127 g_variant_builder_add(&builder, "{sv}", prop->name().c_str(), g_variant_new("v", value));
129 auto sequence = AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, std::string(prop->name()+"Sequence").c_str(), &error, iface);
130 g_variant_builder_add(&builder, "{sv}", std::string(prop->name()+"Sequence").c_str(), g_variant_new("v", sequence));
132 auto quality = AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, std::string(prop->name()+"ValueQuality").c_str(), &error, iface);
133 g_variant_builder_add(&builder, "{sv}", std::string(prop->name()+"ValueQuality").c_str(), g_variant_new("v", quality));
135 auto freq = AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, std::string(prop->name()+"UpdateFrequency").c_str(), &error, iface);
136 g_variant_builder_add(&builder, "{sv}", std::string(prop->name()+"UpdateFrequency").c_str(), g_variant_new("v", freq));
139 auto time = AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, "Time", nullptr, iface);
140 auto zone = AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, "Zone", nullptr, iface);
141 g_variant_builder_add(&builder, "{sv}", "Time", g_variant_new("v", time));
142 g_variant_builder_add(&builder, "{sv}", "Zone", g_variant_new("v", zone));
144 g_dbus_method_invocation_return_value(invocation, g_variant_new("(a{sv})", &builder));
147 else if(method == "Set")
149 gchar* ifaceName = nullptr;
150 gchar* propName = nullptr;
152 g_variant_get(parameters, "(ssv)", &ifaceName, &propName, &value);
154 AbstractDBusInterface::setProperty(connection, sender, object_path, ifaceName, propName, value, nullptr, iface,
155 [&invocation, &ifaceName](bool success, AsyncPropertyReply::Error error) {
158 g_dbus_method_invocation_return_value(invocation, nullptr);
162 g_dbus_method_invocation_return_dbus_error(invocation, ifaceName, AsyncPropertyReply::errorToStr(error).c_str());
168 else if(method == "GetHistory")
170 double beginTime = 0;
173 g_variant_get(parameters, "(dd)", &beginTime, &endTime);
175 auto propertyMap = iface->getProperties();
177 PropertyList propertyList;
179 for(auto itr = propertyMap.begin(); itr != propertyMap.end(); itr++)
181 VariantType* prop = (*itr).second;
183 if(!contains(propertyList, prop->ambPropertyName()))
184 propertyList.push_back(prop->ambPropertyName());
187 std::string ifaceName = iface->interfaceName();
189 AsyncRangePropertyRequest request;
191 request.properties = propertyList;
192 request.timeBegin = beginTime;
193 request.timeEnd = endTime;
194 request.zone = iface->zone();
195 //request.sourceUuid = iface->source();
197 request.completed = [&invocation,&ifaceName](AsyncRangePropertyReply* r)
199 auto reply = amb::make_unique(r);
203 str<<"Error during request: "<<AsyncPropertyReply::errorToStr(reply->error);
204 ifaceName += ".Error";
205 g_dbus_method_invocation_return_dbus_error(invocation, ifaceName.c_str(), str.str().c_str());
209 if(!reply->values.size())
211 ifaceName += ".Error";
212 g_dbus_method_invocation_return_dbus_error(invocation, ifaceName.c_str(), "No results");
216 GVariantBuilder builder;
217 g_variant_builder_init(&builder, G_VARIANT_TYPE("a{svd}"));
220 for(auto itr = reply->values.begin(); itr != reply->values.end(); itr++)
222 AbstractPropertyType* value = *itr;
224 g_variant_builder_add(&builder, "{svd}", value->name.c_str(), g_variant_ref(value->toVariant()),value->timestamp);
227 g_dbus_method_invocation_return_value(invocation,g_variant_new("(a{svd})",&builder));
230 iface->re->getRangePropertyAsync(request);
235 g_dbus_method_invocation_return_error(invocation,G_DBUS_ERROR,G_DBUS_ERROR_UNKNOWN_METHOD, "Unknown method.");
238 AbstractDBusInterface::AbstractDBusInterface(string interfaceName, string objectName,
239 GDBusConnection* connection)
240 : mInterfaceName(interfaceName), mConnection(connection), mPropertyName(objectName), supported(false), zoneFilter(Zone::None), mTime(0), regId(0)
244 mObjectPath = "/" + objectName;
247 AbstractDBusInterface::~AbstractDBusInterface()
251 PropertyList impl = implementedProperties();
253 for(auto itr = impl.begin(); itr != impl.end(); itr++)
255 if(properties.find(*itr) != properties.end())
257 // Deleted in ~DBusSink()
258 //delete properties[*itr];
259 properties.erase(*itr);
263 objectMap.erase(mObjectPath);
266 void AbstractDBusInterface::addProperty(VariantType * property)
268 string nameToLower = property->name();
269 boost::algorithm::to_lower<string>(nameToLower);
273 if(property->access() == VariantType::Read)
275 else if(property->access() == VariantType::Write)
277 else if(property->access() == VariantType::ReadWrite)
278 access = "readwrite";
279 else throw -1; //FIXME: don't throw
281 std::string pn = property->name();
283 ///see which properties are supported:
285 "<property type='"+ string(property->signature()) + "' name='"+ pn +"' access='"+access+"' />"
286 "<property type='i' name='" + pn + "Sequence' access='read' />"
287 "<property type='i' name='" + pn + "ValueQuality' access='read' />"
288 "<property type='i' name='" + pn + "UpdateFrequency' access='read' />";
290 properties[pn] = property;
292 if(!contains(mimplementedProperties, property->ambPropertyName()))
294 std::string pname = property->ambPropertyName();
295 mimplementedProperties.push_back(pname);
299 void AbstractDBusInterface::registerObject()
303 throw std::runtime_error("forgot to call setDBusConnection on AbstractDBusInterface");
306 if(introspectionXml.empty())
308 cerr<<"no interface to export: "<<mInterfaceName<<endl;
312 if(!boost::algorithm::ends_with(introspectionXml,"</node>"))
314 introspectionXml += "</interface>"
320 GDBusNodeInfo* introspection = g_dbus_node_info_new_for_xml(introspectionXml.c_str(), &error);
322 if(!introspection || error)
325 DebugOut(DebugOut::Error)<<"Error in "<<__FILE__<<" - "<<__FUNCTION__<<":"<<__LINE__<<endl;
326 DebugOut(DebugOut::Error)<<error->message<<endl;
327 DebugOut(DebugOut::Error)<<"probably bad xml:"<<endl;
328 DebugOut(DebugOut::Error)<<introspectionXml<<endl;
335 GDBusInterfaceInfo* mInterfaceInfo = g_dbus_node_info_lookup_interface(introspection, mInterfaceName.c_str());
338 const GDBusInterfaceVTable vtable = { handleMyMethodCall, nullptr, nullptr };
342 DebugOut()<<"registering DBus path: "<<mObjectPath<<endl;
344 regId = g_dbus_connection_register_object(mConnection, mObjectPath.c_str(), mInterfaceInfo, &vtable, this, NULL, &error2);
345 g_dbus_node_info_unref(introspection);
348 DebugOut(DebugOut::Error)<<error2->message<<endl;
349 g_error_free(error2);
354 DebugOut(DebugOut::Error)<<"We failed to register on DBus"<<endl;
358 void AbstractDBusInterface::unregisterObject()
362 DebugOut()<<__FUNCTION__<<endl;
363 g_dbus_connection_unregister_object(mConnection, regId);
369 void AbstractDBusInterface::updateValue(VariantType *property)
371 if(mConnection == nullptr)
377 signaller->fireSignal(mConnection, mObjectPath, mInterfaceName, "PropertiesChanged", property);
380 std::list<AbstractDBusInterface *> AbstractDBusInterface::getObjectsForProperty(string object)
382 std::list<AbstractDBusInterface *> l;
383 for(auto itr = objectMap.begin(); itr != objectMap.end(); itr++)
385 AbstractDBusInterface * interface = (*itr).second;
386 if(interface->objectName() == object)
387 l.push_back(interface);
392 list<AbstractDBusInterface *> AbstractDBusInterface::interfaces()
394 std::list<AbstractDBusInterface*> ifaces;
396 for(auto itr = objectMap.begin(); itr != objectMap.end(); itr++)
398 ifaces.push_back((*itr).second);
404 std::vector<std::string> AbstractDBusInterface::supportedInterfaces()
406 std::vector<std::string> ifaces;
408 for(auto itr : objectMap)
410 if(itr.second->isSupported())
411 ifaces.push_back(itr.second->objectName());
417 bool AbstractDBusInterface::implementsProperty(string property)
419 for(auto itr = properties.begin(); itr != properties.end(); itr++)
421 if((*itr).first == property)
430 void AbstractDBusInterface::startRegistration()
432 //unregisterObject();
433 introspectionXml ="<node>" ;
435 "<interface name='org.freedesktop.DBus.Properties'>"
436 "<method name='Get'>"
437 " <arg type='s' direction='in' name='interface' />"
438 " <arg type='s' direction='in' name='property' />"
439 " <arg type='v' direction='out' name='value' />"
441 "<method name='Set'>"
442 " <arg type='s' direction='in' name='interface' />"
443 " <arg type='s' direction='in' name='property' />"
444 " <arg type='v' direction='in' name='value' />"
446 "<method name='GetAll'>"
447 " <arg type='s' direction='in' name='interface' />"
448 " <arg type='a{sv}' direction='out' name='interface' />"
452 "<interface name='"+ mInterfaceName + "' >"
453 "<property type='i' name='Zone' access='read' />"
454 "<property type='d' name='Time' access='read' />"
455 "<method name='GetHistory'>"
456 " <arg type='d' direction='in' name='beginTimestamp' />"
457 " <arg type='d' direction='in' name='endTimestamp' />"
458 " <arg type='a(svd)' direction='out' name='result' />"
462 GVariant* AbstractDBusInterface::getProperty(GDBusConnection* connection, const gchar* sender, const gchar* objectPath, const gchar* interfaceName,
463 const gchar* propertyName, GError** error, gpointer userData)
465 if(DebugOut::getDebugThreshhold() >= 6)
467 DebugOut(6)<<"DBus GetProperty call from: "<<sender<< " pid: " <<getPid(sender)<< " interface: "<<interfaceName<<" property: "<<propertyName<<endl;
468 DebugOut(6)<<"DBus GetProperty call path: "<<objectPath<<endl;
471 std::string pn = propertyName;
474 if(objectMap.find(objectPath) == objectMap.end())
476 DebugOut(DebugOut::Error)<<objectPath<<" is not a valid object path."<<endl;
479 double time = objectMap[objectPath]->time();
481 GVariant* value = g_variant_new("d", time);
485 else if(boost::ends_with(pn, "Sequence"))
487 AbstractDBusInterface* t = static_cast<AbstractDBusInterface*>(userData);
489 int pos = pn.find("Sequence");
491 std::string p = pn.substr(0,pos);
493 VariantType * theProperty = t->property(p);
497 DebugOut(DebugOut::Error)<<"Invalid Sequence property: "<<p<<endl;
501 int sequence = theProperty->sequence();
503 GVariant* value = g_variant_new("i", sequence);
507 else if(boost::ends_with(pn, "ValueQuality"))
509 AbstractDBusInterface* t = static_cast<AbstractDBusInterface*>(userData);
511 int pos = pn.find("ValueQuality");
513 std::string p = pn.substr(0, pos);
515 VariantType * theProperty = t->property(p);
519 DebugOut(DebugOut::Error)<<"Invalid ValueQuality property: "<<p<<endl;
523 int quality = theProperty->value()->valueQuality;
525 GVariant* value = g_variant_new("i", quality);
529 else if(boost::ends_with(pn, "UpdateFrequency"))
531 AbstractDBusInterface* t = static_cast<AbstractDBusInterface*>(userData);
533 int pos = pn.find("UpdateFrequency");
535 std::string p = pn.substr(0, pos);
537 VariantType * theProperty = t->property(p);
541 DebugOut(DebugOut::Error)<<"Invalid UpdateFrequency property: "<<p<<endl;
545 int freq = theProperty->updateFrequency();
547 GVariant* value = g_variant_new("i", freq);
551 else if(pn == "Zone")
553 if(objectMap.find(objectPath) == objectMap.end())
555 DebugOut(DebugOut::Error)<<objectPath<<" is not a valid object path."<<endl;
559 Zone::Type zone = objectMap[objectPath]->zone();
561 GVariant* value = g_variant_new("i",(int)zone);
565 if(objectMap.count(objectPath))
567 GVariant* value = objectMap[objectPath]->getProperty(propertyName);
569 DebugOut(6) << "Returning value for: " << propertyName << endl;
574 DebugOut(DebugOut::Error)<<"No interface for" << interfaceName <<endl;
578 gboolean AbstractDBusInterface::setProperty(GDBusConnection* connection, const gchar* sender, const gchar* objectPath, const gchar* interfaceName,
579 const gchar* propertyName, GVariant* value, GError** error, gpointer userData,
580 std::function<void (bool, AsyncPropertyReply::Error)> callback)
582 if(DebugOut::getDebugThreshhold() >= 6)
584 DebugOut(6)<<"DBus SetProperty call from: "<<sender<< " pid: " <<getPid(sender)<< " interface: "<<interfaceName<<" property: "<<propertyName<<endl;
585 DebugOut(6)<<"DBus SetProperty call path: "<<objectPath<<endl;
588 if(objectMap.count(objectPath))
590 objectMap[objectPath]->setProperty(propertyName, value, callback);
597 void AbstractDBusInterface::setProperty(string propertyName, GVariant *value, std::function<void (bool, AsyncPropertyReply::Error)> callback)
599 if(properties.count(propertyName))
601 properties[propertyName]->fromVariant(value, callback);
605 callback(false, AsyncPropertyReply::InvalidOperation);
609 GVariant *AbstractDBusInterface::getProperty(string propertyName)
611 if(properties.count(propertyName))
612 return properties[propertyName]->toVariant();
617 void AbstractDBusInterface::setTimeout(int timeout)
620 signaller = DBusSignaller::factory(timeout);