f8af9bc0d2e7bbf532bab4b236501664fdc28250
[platform/core/uifw/dali-adaptor.git] / dali / dali-bridge / src / DBus.cpp
1 /*
2  * Copyright 2017  Samsung Electronics Co., Ltd
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *  http://www.apache.org/licenses/LICENSE-2.0
9
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "DBus.hpp"
18 #include <sstream>
19 //#include "Atspi.hpp"
20 #include <iostream>
21 #include <mutex>
22
23 #define DBUS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties"
24
25 #undef EINA_FALSE
26 #undef EINA_TRUE
27 #define EINA_TRUE static_cast< Eina_Bool >( 1 )
28 #define EINA_FALSE static_cast< Eina_Bool >( 0 )
29
30 //#define DBUS_DEBUG(...) do { DBus::debugPrint(__FILE__, __LINE__, __VA_ARGS__); } while (0)
31
32 std::atomic< unsigned int > DBus::detail::CallId::LastId{0};
33 static std::function< void( const char*, size_t ) > debugPrinter;
34 static std::function< void( DBus::DBusAction ) > notificationCallback;
35 static std::mutex debugLock, notificationLock;
36
37 DBus::detail::CallOnDestructionList& DBus::detail::CallOnDestructionList::operator=( DBus::detail::CallOnDestructionList&& d )
38 {
39   for( auto& q : functions )
40   {
41     q();
42   }
43   functions = std::move( d.functions );
44   d.functions.clear();
45   return *this;
46 }
47
48 void DBus::detail::CallOnDestructionList::add( const std::function< void() >& c )
49 {
50   functions.push_back( c );
51 }
52
53 void DBus::setDBusActionNotifier( std::function< void( DBus::DBusAction ) > callback )
54 {
55   std::lock_guard< std::mutex > lock( notificationLock );
56   notificationCallback = std::move( callback );
57 }
58
59 void DBus::detail::emitNotification( const char* bus, const char* path, const char* interface, const char* member, DBus::DBusActionType type )
60 {
61   std::lock_guard< std::mutex > lock( notificationLock );
62   if( notificationCallback )
63   {
64     notificationCallback( DBusAction{type, bus, path, interface, member} );
65   }
66 }
67
68 void DBus::setDebugPrinter( std::function< void( const char*, size_t ) > printer )
69 {
70   std::lock_guard< std::mutex > lock( debugLock );
71   debugPrinter = std::move( printer );
72 }
73
74 void DBus::debugPrint( const char* file, size_t line, const char* format, ... )
75 {
76   std::function< void( const char*, size_t ) > debugPrintFunc;
77   {
78     std::lock_guard< std::mutex > lock( debugLock );
79     if( !debugPrinter )
80       return;
81     debugPrintFunc = debugPrinter;
82   }
83   std::vector< char > buf( 4096 );
84   int offset;
85   while( true )
86   {
87     offset = snprintf( buf.data(), buf.size(), "%s:%u: ", file, static_cast< unsigned int >( line ) );
88     if( offset < 0 )
89       return;
90     if( static_cast< size_t >( offset ) < buf.size() )
91       break;
92     buf.resize( offset + 1 );
93   }
94
95   while( true )
96   {
97     va_list args;
98     va_start( args, format );
99     int z = vsnprintf( buf.data() + offset, buf.size() - offset, format, args );
100     va_end( args );
101     if( z < 0 )
102       return;
103     bool done = static_cast< size_t >( z ) + static_cast< size_t >( offset ) < buf.size();
104     buf.resize( static_cast< size_t >( z ) + static_cast< size_t >( offset ) );
105     if( done )
106       break;
107   }
108   debugPrintFunc( buf.data(), buf.size() );
109 }
110
111 std::shared_ptr< DBus::EldbusConnection > DBus::getDBusConnectionByName( const std::string& name )
112 {
113   eldbus_init();
114   auto z = getDBusConnectionByType( ConnectionType::SYSTEM );
115   auto connection = eldbus_address_connection_get( name.c_str() );
116   auto ptr = std::make_shared< EldbusConnection >( connection );
117   eldbus_shutdown();
118   return ptr;
119 }
120
121 std::shared_ptr< DBus::EldbusConnection > DBus::getDBusConnectionByType( ConnectionType connectionType )
122 {
123   Eldbus_Connection_Type eldbusType = ELDBUS_CONNECTION_TYPE_SYSTEM;
124
125   switch( connectionType )
126   {
127     case ConnectionType::SYSTEM:
128     {
129       eldbusType = ELDBUS_CONNECTION_TYPE_SYSTEM;
130       break;
131     }
132     case ConnectionType::SESSION:
133     {
134       eldbusType = ELDBUS_CONNECTION_TYPE_SESSION;
135       break;
136     }
137   }
138
139   eldbus_init();
140   auto connection = eldbus_connection_get( eldbusType );
141   auto ptr = std::make_shared< EldbusConnection >( connection );
142   eldbus_shutdown();
143   return ptr;
144 }
145
146 DBus::DBusClient::DBusClient( std::string busName, std::string pathName, std::string interfaceName, ConnectionType tp ) : DBusClient( std::move( busName ), std::move( pathName ), std::move( interfaceName ), getDBusConnectionByType( tp ) )
147 {
148 }
149
150 struct caller_eldbus_object_unref
151 {
152   void operator()( Eldbus_Object* p ) const
153   {
154     eldbus_object_unref( p );
155   }
156 };
157
158 DBus::DBusClient::DBusClient( std::string busName, std::string pathName, std::string interfaceName, const std::shared_ptr< DBus::EldbusConnection >& conn )
159 {
160   if( !conn )
161     connectionState.connection = getDBusConnectionByType( ConnectionType::SESSION );
162   else
163     connectionState.connection = conn;
164
165   std::ostringstream o;
166   o << "bus = " << busName << " path = " << pathName << " connection = " << eldbus_connection_unique_name_get( connectionState.connection->get() );
167   info = o.str();
168
169   auto c = connectionState.connection;
170   connectionState.object = {
171       eldbus_object_get( connectionState.connection->get(), busName.c_str(), pathName.c_str() ),
172       [c]( Eldbus_Object* p ) {
173         eldbus_object_unref( p );
174       }};
175   if( connectionState.object )
176   {
177     auto obj = connectionState.object;
178     connectionState.proxy = {
179         eldbus_proxy_get( connectionState.object.get(), interfaceName.c_str() ),
180         [obj]( Eldbus_Proxy* p ) {
181           eldbus_proxy_unref( p );
182         }};
183     if( interfaceName != DBUS_INTERFACE_PROPERTIES )
184     {
185       auto obj = connectionState.object;
186       connectionState.propertiesProxy = {
187           eldbus_proxy_get( connectionState.object.get(), DBUS_INTERFACE_PROPERTIES ),
188           [obj]( Eldbus_Proxy* p ) {
189             eldbus_proxy_unref( p );
190           }};
191     }
192     else
193     {
194       connectionState.propertiesProxy = connectionState.proxy;
195     }
196   }
197   connectionInfo = std::make_shared< ConnectionInfo >();
198   connectionInfo->busName = std::move( busName );
199   connectionInfo->pathName = std::move( pathName );
200   connectionInfo->interfaceName = std::move( interfaceName );
201 }
202
203 DBus::DBusServer::DBusServer( ConnectionType tp ) : DBus::DBusServer( DBus::getDBusConnectionByType( tp ) )
204 {
205 }
206
207 DBus::DBusServer::DBusServer( const std::shared_ptr< DBus::EldbusConnection >& conn )
208 {
209   if( !conn )
210     connection = getDBusConnectionByType( ConnectionType::SESSION );
211   else
212     connection = conn;
213 }
214
215 DBus::DBusInterfaceDescription::DBusInterfaceDescription( std::string interfaceName ) : interfaceName( std::move( interfaceName ) )
216 {
217 }
218
219 struct Implementation
220 {
221   Eldbus_Service_Interface_Desc dsc;
222   std::vector< Eldbus_Method > methods;
223   std::vector< Eldbus_Signal > signals;
224   std::vector< Eldbus_Property > properties;
225   DBus::detail::StringStorage strings;
226
227   std::unordered_map< std::string, DBus::DBusInterfaceDescription::MethodInfo > methodsMap;
228   std::unordered_map< std::string, DBus::DBusInterfaceDescription::PropertyInfo > propertiesMap;
229   std::unordered_map< unsigned int, DBus::DBusInterfaceDescription::SignalInfo > signalsMap;
230
231   std::shared_ptr< DBus::EldbusConnection > connection;
232 };
233
234 static std::unordered_map< const Eldbus_Service_Interface*, std::unique_ptr< Implementation > > globalEntries;
235 static std::mutex globalEntriesMutex;
236 static thread_local const char* currentObjectPath = "";
237 static thread_local std::shared_ptr< DBus::EldbusConnection > currentConnection;
238
239 class CurrentObjectSetter
240 {
241 public:
242   CurrentObjectSetter( std::shared_ptr< DBus::EldbusConnection > con, const Eldbus_Message* m )
243   {
244     currentObjectPath = eldbus_message_path_get( m );
245     currentConnection = std::move( con );
246   }
247   ~CurrentObjectSetter()
248   {
249     currentObjectPath = "";
250     currentConnection = {};
251   }
252   CurrentObjectSetter( const CurrentObjectSetter& ) = delete;
253   CurrentObjectSetter( CurrentObjectSetter&& ) = delete;
254   void operator=( const CurrentObjectSetter& ) = delete;
255   void operator=( CurrentObjectSetter&& ) = delete;
256 };
257
258 std::string DBus::DBusServer::getCurrentObjectPath()
259 {
260   return currentObjectPath;
261 }
262
263 std::shared_ptr< DBus::EldbusConnection > DBus::DBusServer::getCurrentConnection()
264 {
265   return currentConnection;
266 }
267
268 static Eina_Bool property_get_callback( const Eldbus_Service_Interface* iface, const char* propertyName, Eldbus_Message_Iter* iter,
269                                         const Eldbus_Message* message, Eldbus_Message** error )
270 {
271   Implementation* impl = nullptr;
272   {
273     std::lock_guard< std::mutex > lock( globalEntriesMutex );
274     auto it = globalEntries.find( iface );
275     if( it != globalEntries.end() )
276       impl = it->second.get();
277   }
278   if( !impl )
279     return EINA_FALSE;
280
281   auto it = impl->propertiesMap.find( propertyName );
282   if( it == impl->propertiesMap.end() || !it->second.getCallback )
283     return EINA_FALSE;
284
285   CurrentObjectSetter currentObjectSetter( impl->connection, message );
286   auto reply = it->second.getCallback( message, iter );
287   if( !reply )
288   {
289     if( error )
290       *error = eldbus_message_error_new( message, "org.freedesktop.DBus.Error.Failed", reply.getError().message.c_str() );
291     return EINA_FALSE;
292   }
293
294   return EINA_TRUE;
295 }
296
297 static Eldbus_Message* property_set_callback( const Eldbus_Service_Interface* iface, const char* propertyName, Eldbus_Message_Iter* iter,
298                                               const Eldbus_Message* message )
299 {
300   Implementation* impl = nullptr;
301   {
302     std::lock_guard< std::mutex > lock( globalEntriesMutex );
303     auto it = globalEntries.find( iface );
304     if( it != globalEntries.end() )
305       impl = it->second.get();
306   }
307   if( !impl )
308   {
309     auto ret = eldbus_message_error_new( message, "org.freedesktop.DBus.Error.Failed", "Unknown interface" );
310     return ret;
311   }
312   auto it = impl->propertiesMap.find( propertyName );
313   if( it == impl->propertiesMap.end() || !it->second.setCallback )
314   {
315     auto ret = eldbus_message_error_new( message, "org.freedesktop.DBus.Error.Failed", "Unknown setter" );
316     return ret;
317   }
318   CurrentObjectSetter currentObjectSetter( impl->connection, message );
319   auto reply = it->second.setCallback( message, iter );
320
321   Eldbus_Message* ret = nullptr;
322   if( !reply )
323   {
324     ret = eldbus_message_error_new( message, "org.freedesktop.DBus.Error.Failed", reply.getError().message.c_str() );
325   }
326   else
327   {
328     ret = eldbus_message_method_return_new( message );
329   }
330   return ret;
331 }
332
333 static Eldbus_Message* method_callback( const Eldbus_Service_Interface* iface, const Eldbus_Message* message )
334 {
335   Implementation* impl = nullptr;
336   {
337     std::lock_guard< std::mutex > lock( globalEntriesMutex );
338     auto it = globalEntries.find( iface );
339     if( it != globalEntries.end() )
340       impl = it->second.get();
341   }
342   if( !impl )
343   {
344     auto ret = eldbus_message_error_new( message, "org.freedesktop.DBus.Error.Failed", "Unknown interface" );
345     return ret;
346   }
347   std::string memberName = eldbus_message_member_get( message );
348   auto it = impl->methodsMap.find( memberName );
349   if( it == impl->methodsMap.end() )
350   {
351     auto ret = eldbus_message_error_new( message, "org.freedesktop.DBus.Error.Failed", "Unknown method" );
352     return ret;
353   }
354   CurrentObjectSetter currentObjectSetter( impl->connection, message );
355   auto reply = it->second.callback( message );
356   return reply;
357 }
358
359 static void addInterfaceImpl( bool fallback, const std::string& pathName,
360                               const std::shared_ptr< DBus::EldbusConnection >& connection,
361                               const std::string& interfaceName,
362                               std::unordered_map< unsigned int, std::pair< const Eldbus_Service_Interface*, unsigned int > >& signalData,
363                               DBus::detail::StringStorage& strings,
364                               std::vector< DBus::DBusInterfaceDescription::MethodInfo >& dscrMethods,
365                               std::vector< DBus::DBusInterfaceDescription::PropertyInfo >& dscrProperties,
366                               std::vector< DBus::DBusInterfaceDescription::SignalInfo >& dscrSignals,
367                               DBus::detail::CallOnDestructionList& destructors )
368 {
369   std::vector< Eldbus_Method > methods;
370   std::vector< Eldbus_Signal > signals;
371   std::vector< Eldbus_Property > properties;
372   std::unordered_map< std::string, DBus::DBusInterfaceDescription::MethodInfo > methodsMap;
373   std::unordered_map< std::string, DBus::DBusInterfaceDescription::PropertyInfo > propertiesMap;
374   std::unordered_map< unsigned int, DBus::DBusInterfaceDescription::SignalInfo > signalsMap;
375
376   DBUS_DEBUG( "interface %s path %s on bus %s", interfaceName.c_str(), pathName.c_str(), DBus::getConnectionName( connection ).c_str() );
377   for( auto& ee : dscrMethods )
378   {
379     auto key = ee.memberName;
380     DBUS_DEBUG( "adding method %s", ee.memberName.c_str() );
381     for( auto& r : ee.in )
382     {
383       if( !r.name )
384         break;
385       DBUS_DEBUG( "in %s '%s'", r.name, r.signature );
386     }
387     for( auto& r : ee.out )
388     {
389       if( !r.name )
390         break;
391       DBUS_DEBUG( "out %s '%s'", r.name, r.signature );
392     }
393     auto& e = ( methodsMap[key] = std::move( ee ) );
394     methods.push_back( {} );
395     auto& m = methods.back();
396     m.member = e.memberName.c_str();
397     m.in = e.in.data();
398     m.out = e.out.data();
399     m.cb = method_callback;
400     m.flags = 0;
401   }
402   for( auto& ee : dscrProperties )
403   {
404     auto key = ee.memberName;
405     DBUS_DEBUG( "adding property %s", ee.memberName.c_str() );
406     auto& e = ( propertiesMap[key] = std::move( ee ) );
407     properties.push_back( {} );
408     auto& m = properties.back();
409     m.name = e.memberName.c_str();
410     m.type = e.typeSignature.c_str();
411     m.get_func = e.getCallback ? property_get_callback : nullptr;
412     m.set_func = e.setCallback ? property_set_callback : nullptr;
413     m.flags = 0;
414   }
415   unsigned int signalIndex = 0;
416   std::vector< unsigned int > signalIds;
417   for( auto& ee : dscrSignals )
418   {
419     DBUS_DEBUG( "adding signal %s", ee.memberName.c_str() );
420     auto& e = ( signalsMap[ee.id.id] = std::move( ee ) );
421     signals.push_back( {} );
422     auto& m = signals.back();
423     m.name = e.memberName.c_str();
424     m.args = e.args.data();
425     m.flags = 0;
426     signalData[e.id.id].second = signalIndex++;
427     signalIds.push_back( e.id.id );
428   }
429   dscrMethods.clear();
430   dscrProperties.clear();
431   dscrSignals.clear();
432
433   methods.push_back( {nullptr, nullptr, nullptr, nullptr, 0} );
434   signals.push_back( {nullptr, nullptr, 0} );
435   properties.push_back( {nullptr, nullptr, nullptr, nullptr, 0} );
436
437   auto impl = std::unique_ptr< Implementation >( new Implementation{
438       {interfaceName.c_str(),
439        methods.data(),
440        signals.data(),
441        properties.data(),
442        nullptr,
443        nullptr},
444       std::move( methods ),
445       std::move( signals ),
446       std::move( properties ),
447       std::move( strings ),
448       std::move( methodsMap ),
449       std::move( propertiesMap ),
450       std::move( signalsMap ),
451       connection} );
452
453   {
454     std::lock_guard< std::mutex > lock( globalEntriesMutex );
455     auto v = fallback ? eldbus_service_interface_fallback_register( connection->get(), pathName.c_str(), &impl->dsc ) : eldbus_service_interface_register( connection->get(), pathName.c_str(), &impl->dsc );
456     assert( v );
457     globalEntries[v] = std::move( impl );
458     DBUS_DEBUG( "registering interface %p (%d)", v, fallback ? 1 : 0 );
459     destructors.add( [=]() {
460       eldbus_service_interface_unregister( v );
461       std::lock_guard< std::mutex > lock( globalEntriesMutex );
462       globalEntries.erase( v );
463       DBUS_DEBUG( "unregistering interface %p (%d)", v, fallback ? 1 : 0 );
464     } );
465     for( auto id : signalIds )
466     {
467       signalData[id].first = v;
468     }
469   }
470 }
471
472 std::shared_ptr< DBus::EldbusConnection > DBus::DBusServer::getConnection()
473 {
474   return connection;
475 }
476
477 void DBus::DBusServer::addInterface( const std::string& pathName, DBusInterfaceDescription& dscr, bool fallback )
478 {
479   addInterfaceImpl( fallback, pathName, connection, dscr.interfaceName, signalData, dscr.strings, dscr.methods, dscr.properties, dscr.signals, destructors );
480 }
481
482 std::string DBus::DBusServer::getBusName() const
483 {
484   return getConnectionName( connection );
485 }
486
487 std::string DBus::getConnectionName( const std::shared_ptr< DBus::EldbusConnection >& c )
488 {
489   return eldbus_connection_unique_name_get( c->get() );
490 }