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));
133 auto time = AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, "Time", nullptr, iface);
134 auto zone = AbstractDBusInterface::getProperty(connection, sender, object_path, ifaceName, "Zone", nullptr, iface);
135 g_variant_builder_add(&builder, "{sv}", "Time", g_variant_new("v", time));
136 g_variant_builder_add(&builder, "{sv}", "Zone", g_variant_new("v", zone));
138 g_dbus_method_invocation_return_value(invocation, g_variant_new("(a{sv})", &builder));
141 else if(method == "Set")
143 gchar* ifaceName = nullptr;
144 gchar* propName = nullptr;
146 g_variant_get(parameters, "(ssv)", &ifaceName, &propName, &value);
148 AbstractDBusInterface::setProperty(connection, sender, object_path, ifaceName, propName, value, nullptr, iface,
149 [&invocation, &ifaceName](bool success, AsyncPropertyReply::Error error) {
152 g_dbus_method_invocation_return_value(invocation, nullptr);
156 g_dbus_method_invocation_return_dbus_error(invocation, ifaceName, AsyncPropertyReply::errorToStr(error).c_str());
162 else if(method == "GetHistory")
164 double beginTime = 0;
167 g_variant_get(parameters, "(dd)", &beginTime, &endTime);
169 auto propertyMap = iface->getProperties();
171 PropertyList propertyList;
173 for(auto itr = propertyMap.begin(); itr != propertyMap.end(); itr++)
175 VariantType* prop = (*itr).second;
177 if(!contains(propertyList, prop->ambPropertyName()))
178 propertyList.push_back(prop->ambPropertyName());
181 std::string ifaceName = iface->interfaceName();
183 AsyncRangePropertyRequest request;
185 request.properties = propertyList;
186 request.timeBegin = beginTime;
187 request.timeEnd = endTime;
188 request.zone = iface->zone();
189 //request.sourceUuid = iface->source();
191 request.completed = [&invocation,&ifaceName](AsyncRangePropertyReply* r)
193 auto reply = amb::make_unique(r);
197 str<<"Error during request: "<<AsyncPropertyReply::errorToStr(reply->error);
198 ifaceName += ".Error";
199 g_dbus_method_invocation_return_dbus_error(invocation, ifaceName.c_str(), str.str().c_str());
203 if(!reply->values.size())
205 ifaceName += ".Error";
206 g_dbus_method_invocation_return_dbus_error(invocation, ifaceName.c_str(), "No results");
210 GVariantBuilder builder;
211 g_variant_builder_init(&builder, G_VARIANT_TYPE("a{svd}"));
214 for(auto itr = reply->values.begin(); itr != reply->values.end(); itr++)
216 AbstractPropertyType* value = *itr;
218 g_variant_builder_add(&builder, "{svd}", value->name.c_str(), g_variant_ref(value->toVariant()),value->timestamp);
221 g_dbus_method_invocation_return_value(invocation,g_variant_new("(a{svd})",&builder));
224 iface->re->getRangePropertyAsync(request);
229 ///TODO: Deprecated in 0.15
230 else if(boost::algorithm::starts_with(method, "Get"))
232 amb::deprecateMethod(method, "0.15");
233 std::string propertyName = method.substr(3);
234 auto propertyMap = iface->getProperties();
235 if(propertyMap.find(propertyName) == propertyMap.end())
237 g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "Unknown method.");
241 VariantType * property = propertyMap[propertyName];
243 GError *error = NULL;
245 GVariant **params = g_new(GVariant*,4);
246 GVariant *val = g_variant_ref(property->value()->toVariant());
247 params[0] = g_variant_new("v", val);
248 params[1] = g_variant_new("d",property->timestamp());
249 params[2] = g_variant_new("i",property->value()->sequence);
250 params[3] = g_variant_new("i",property->updateFrequency());
252 GVariant *tuple_variant = g_variant_new_tuple(params,4);
254 g_dbus_method_invocation_return_value(invocation, tuple_variant);
257 g_variant_unref(val);
261 DebugOut(DebugOut::Error)<<error->message<<endl;
267 g_dbus_method_invocation_return_error(invocation,G_DBUS_ERROR,G_DBUS_ERROR_UNKNOWN_METHOD, "Unknown method.");
270 AbstractDBusInterface::AbstractDBusInterface(string interfaceName, string objectName,
271 GDBusConnection* connection)
272 : mInterfaceName(interfaceName), mConnection(connection), mPropertyName(objectName), supported(false), zoneFilter(Zone::None), mTime(0), regId(0)
276 mObjectPath = "/" + objectName;
279 AbstractDBusInterface::~AbstractDBusInterface()
283 PropertyList impl = implementedProperties();
285 for(auto itr = impl.begin(); itr != impl.end(); itr++)
287 if(properties.find(*itr) != properties.end())
289 // Deleted in ~DBusSink()
290 //delete properties[*itr];
291 properties.erase(*itr);
295 objectMap.erase(mObjectPath);
298 void AbstractDBusInterface::addProperty(VariantType * property)
300 string nameToLower = property->name();
301 boost::algorithm::to_lower<string>(nameToLower);
305 if(property->access() == VariantType::Read)
307 else if(property->access() == VariantType::Write)
309 else if(property->access() == VariantType::ReadWrite)
310 access = "readwrite";
311 else throw -1; //FIXME: don't throw
313 std::string pn = property->name();
315 ///see which properties are supported:
317 "<property type='"+ string(property->signature()) + "' name='"+ pn +"' access='"+access+"' />"
318 "<!-- 'GetFoo' is deprecated as of 0.14 -->"
319 /// TODO: remove GetFoo in 0.15
320 "<method name='Get" + pn + "'>"
321 " <arg type='v' direction='out' name='value' />"
322 " <arg type='d' direction='out' name='timestamp' />"
323 " <arg type='i' direction='out' name='sequence' />"
324 " <arg type='i' direction='out' name='updateFrequency' />"
326 "<property type='i' name='" + pn + "Sequence' access='read' />";
328 properties[pn] = property;
330 if(!contains(mimplementedProperties, property->ambPropertyName()))
332 std::string pname = property->ambPropertyName();
333 mimplementedProperties.push_back(pname);
337 void AbstractDBusInterface::registerObject()
341 throw std::runtime_error("forgot to call setDBusConnection on AbstractDBusInterface");
344 if(introspectionXml.empty())
346 cerr<<"no interface to export: "<<mInterfaceName<<endl;
350 if(!boost::algorithm::ends_with(introspectionXml,"</node>"))
352 introspectionXml += "</interface>"
358 GDBusNodeInfo* introspection = g_dbus_node_info_new_for_xml(introspectionXml.c_str(), &error);
360 if(!introspection || error)
363 DebugOut(DebugOut::Error)<<"Error in "<<__FILE__<<" - "<<__FUNCTION__<<":"<<__LINE__<<endl;
364 DebugOut(DebugOut::Error)<<error->message<<endl;
365 DebugOut(DebugOut::Error)<<"probably bad xml:"<<endl;
366 DebugOut(DebugOut::Error)<<introspectionXml<<endl;
373 GDBusInterfaceInfo* mInterfaceInfo = g_dbus_node_info_lookup_interface(introspection, mInterfaceName.c_str());
376 const GDBusInterfaceVTable vtable = { handleMyMethodCall, nullptr, nullptr };
380 DebugOut()<<"registering DBus path: "<<mObjectPath<<endl;
382 regId = g_dbus_connection_register_object(mConnection, mObjectPath.c_str(), mInterfaceInfo, &vtable, this, NULL, &error2);
383 g_dbus_node_info_unref(introspection);
386 DebugOut(DebugOut::Error)<<error2->message<<endl;
387 g_error_free(error2);
392 DebugOut(DebugOut::Error)<<"We failed to register on DBus"<<endl;
396 void AbstractDBusInterface::unregisterObject()
400 DebugOut()<<__FUNCTION__<<endl;
401 g_dbus_connection_unregister_object(mConnection, regId);
407 void AbstractDBusInterface::updateValue(VariantType *property)
409 if(mConnection == nullptr)
415 signaller->fireSignal(mConnection, mObjectPath, mInterfaceName, "PropertiesChanged", property);
418 std::list<AbstractDBusInterface *> AbstractDBusInterface::getObjectsForProperty(string object)
420 std::list<AbstractDBusInterface *> l;
421 for(auto itr = objectMap.begin(); itr != objectMap.end(); itr++)
423 AbstractDBusInterface * interface = (*itr).second;
424 if(interface->objectName() == object)
425 l.push_back(interface);
430 list<AbstractDBusInterface *> AbstractDBusInterface::interfaces()
432 std::list<AbstractDBusInterface*> ifaces;
434 for(auto itr = objectMap.begin(); itr != objectMap.end(); itr++)
436 ifaces.push_back((*itr).second);
442 std::vector<std::string> AbstractDBusInterface::supportedInterfaces()
444 std::vector<std::string> ifaces;
446 for(auto itr : objectMap)
448 if(itr.second->isSupported())
449 ifaces.push_back(itr.second->objectName());
455 bool AbstractDBusInterface::implementsProperty(string property)
457 for(auto itr = properties.begin(); itr != properties.end(); itr++)
459 if((*itr).first == property)
468 void AbstractDBusInterface::startRegistration()
470 //unregisterObject();
471 introspectionXml ="<node>" ;
473 "<interface name='org.freedesktop.DBus.Properties'>"
474 "<method name='Get'>"
475 " <arg type='s' direction='in' name='interface' />"
476 " <arg type='s' direction='in' name='property' />"
477 " <arg type='v' direction='out' name='value' />"
479 "<method name='Set'>"
480 " <arg type='s' direction='in' name='interface' />"
481 " <arg type='s' direction='in' name='property' />"
482 " <arg type='v' direction='in' name='value' />"
484 "<method name='GetAll'>"
485 " <arg type='s' direction='in' name='interface' />"
486 " <arg type='a{sv}' direction='out' name='interface' />"
490 "<interface name='"+ mInterfaceName + "' >"
491 "<property type='i' name='Zone' access='read' />"
492 "<property type='d' name='Time' access='read' />"
493 "<method name='GetHistory'>"
494 " <arg type='d' direction='in' name='beginTimestamp' />"
495 " <arg type='d' direction='in' name='endTimestamp' />"
496 " <arg type='a(svd)' direction='out' name='result' />"
500 GVariant* AbstractDBusInterface::getProperty(GDBusConnection* connection, const gchar* sender, const gchar* objectPath, const gchar* interfaceName,
501 const gchar* propertyName, GError** error, gpointer userData)
503 if(DebugOut::getDebugThreshhold() >= 6)
505 DebugOut(6)<<"DBus GetProperty call from: "<<sender<< " pid: " <<getPid(sender)<< " interface: "<<interfaceName<<" property: "<<propertyName<<endl;
506 DebugOut(6)<<"DBus GetProperty call path: "<<objectPath<<endl;
509 std::string pn = propertyName;
512 if(objectMap.find(objectPath) == objectMap.end())
514 DebugOut(DebugOut::Error)<<objectPath<<" is not a valid object path."<<endl;
517 double time = objectMap[objectPath]->time();
519 GVariant* value = g_variant_new("d", time);
523 if(boost::ends_with(pn, "Sequence"))
525 AbstractDBusInterface* t = static_cast<AbstractDBusInterface*>(userData);
527 int pos = pn.find("Sequence");
529 std::string p = pn.substr(0,pos);
531 VariantType * theProperty = t->property(p);
535 DebugOut(DebugOut::Error)<<"Invalid Sequence property: "<<p<<endl;
539 int sequence = theProperty->sequence();
541 GVariant* value = g_variant_new("i", sequence);
547 if(objectMap.find(objectPath) == objectMap.end())
549 DebugOut(DebugOut::Error)<<objectPath<<" is not a valid object path."<<endl;
553 Zone::Type zone = objectMap[objectPath]->zone();
555 GVariant* value = g_variant_new("i",(int)zone);
559 if(objectMap.count(objectPath))
561 GVariant* value = objectMap[objectPath]->getProperty(propertyName);
563 DebugOut(6) << "Returning value for: " << propertyName << endl;
568 DebugOut(DebugOut::Error)<<"No interface for" << interfaceName <<endl;
572 gboolean AbstractDBusInterface::setProperty(GDBusConnection* connection, const gchar* sender, const gchar* objectPath, const gchar* interfaceName,
573 const gchar* propertyName, GVariant* value, GError** error, gpointer userData,
574 std::function<void (bool, AsyncPropertyReply::Error)> callback)
576 if(DebugOut::getDebugThreshhold() >= 6)
578 DebugOut(6)<<"DBus SetProperty call from: "<<sender<< " pid: " <<getPid(sender)<< " interface: "<<interfaceName<<" property: "<<propertyName<<endl;
579 DebugOut(6)<<"DBus SetProperty call path: "<<objectPath<<endl;
582 if(objectMap.count(objectPath))
584 objectMap[objectPath]->setProperty(propertyName, value, callback);
591 void AbstractDBusInterface::setProperty(string propertyName, GVariant *value, std::function<void (bool, AsyncPropertyReply::Error)> callback)
593 if(properties.count(propertyName))
595 properties[propertyName]->fromVariant(value, callback);
599 callback(false, AsyncPropertyReply::InvalidOperation);
603 GVariant *AbstractDBusInterface::getProperty(string propertyName)
605 if(properties.count(propertyName))
606 return properties[propertyName]->toVariant();
611 void AbstractDBusInterface::setTimeout(int timeout)
614 signaller = DBusSignaller::factory(timeout);