aboutsummaryrefslogblamecommitdiffstats
path: root/src/lib/eldbus/eldbus_core.c
blob: 6ab6a03a8a82049e66d85106765536fea23884cd (plain) (tree)
1
2
3
4
5
6
7
8



                    

                                 
 
                                                              


                                                             
                                                              
                                                             
                                                              





                                                             
                                                                      


                                                                     
                                                                      
                                                                     
                                                                      





                                                                     
                                                  

               
                                 

                                         
                                     
 
                                                

               
                                   

                                           
                                     
                                                 
                                   
 
                                   



                                
                           

                             
                      
 
                                   



                             
                           
                              
                      
 

                                                                
 

                                  
                              
 
                                                                            
                       

                                                                 
 


                                                                                                                                       
                                                             

        
                 
 

                                


                    
                                                             


                 

                                                                         
      
                                                                



                        









                                                                                    







                                                                                     
 
 





                                                                 
 
                             

               
                           
             
                            
              
                             
               
                                    
                      
                             
               

                                                    

                                               





                   
                                              





                                                                       
                                     

                                         
                                















                                                                      
                          
                                              

                                                                      



                                                



        
                     
 
                               

                                                          
                               

                 

                               
 
                                                              
      
                                             
                                                                                      
      
                                                             
      
                                            
                                                                                     
      
                                                              
      
                                             
                                                                                      
      



                                                              





                                                                    
                                                                              


                                                     




                                                                                            
 





                                    
 

                                                    

                                               




                   

                                             

               
                     
                      
                         

    
                                                                       





                                      
                                    
 
                                                                     







                                                 
                                                                          
 
                                                                        








                                                        
                                                                          
 
                               
 
                                












                                                            
                           




                       
              
 

                                                      

                                     
                  
 
                                 











                                                                        
                                                                       
 
                                                 







                                                                
                                                









                                                            
                                                     
 
                                                 



                                     
                                                     
 
                                                 










                                                           
                                         





                            
                                                                   





                                                
           
                                                                              
 




                                                            
            
 

                                            
                                                       


                                  
                          
                                               

                                         
                                                


            
    
                                                                                    
 
                                                                       




                                              
                                       


    
                                                                              
 
                              
 
                                                    

                                              
          


           
                                                            
 
                                       
                                     
 
                                                                           



                                                                             
 
                                                    
                                                   


           
                                                                                             
 
                                           
                                     
 

                             
                                                        
      
                                                               

                                                                    
      
                                                                
                                                      

                                                   
                                               

 
    
                                                      
 
                                  


                  
    
                                                                                 
 
                                 


                                  
                                       

 

                                                                     
 

                                              



                                          
                                                  
                                            
                                         
                                                      
 
                                     



                                                   

                      

                                                
                                                                                       
 


                                                                            

                                                                                 
                                                                        


                                                        



                                                   

                                                                                              
 
                              
 
                                              







                                            
                                              


                               
                                              


                                                                    



                                                  




                
                                                           
 
                                  


















                                                                                                      
                                              















                                                              
                                                                





                                                    
                                    
 

                                  




                                          

                                  




                                                                    
                                               
                                                  
                                                            






                                                                
                             




                    
                                                      

                       
                                                



                                          
                                                         
 
                           
                                   
                   




                                                              








                                                  


           
                                            
 
                                          







                                                                
                                  
 
                                  















                                                            

                                  




                                                 
                                               

                                                  
                                                                        


                                                     
                                                                                    






                                                            
                                                            

                       
                                                



                                              
                                                               
 
                           







                                                          
                                                                      












                                                                     
                        
 
                                  

                                                  
                                                             
 

                                                              

                                                                   
                               


                                     
                                           
                 


                                             
                     



                               
                                                                                                    
 
                                  
 





                                                                  



                                                                    
                                                          
                                                                               
                               



                                                                           
                               





                                     
                       
                                                                  



























                                                                        
           
                                                               
 
                              
                     
 

                                               
 


                                                                
 

                               






                                                                  
      
                                  
 
                                                                    

                          
                                   









                                                                          
                                                                       

                                                                        

                                                                             
                                                                              
                                                      
 

                                                

                                                                          
                                        
 



                                                                             

                                                                     

      


                                    


                        
                                                                                           
 
                                       

















                                                                    
                                           


                                                                          



                                                                    





                                              
                                                





















                                                                       
                                                                          





                                                                           
           
                                                                
 
                                  

                               

                                                        






                                                            

 
                                                                        
                          
                                                                                   
 
                           
                 
                      
 

                                                                                  

                                                                             
 
                                               


                                              




                                                                    
                                                   

                   

                                                                                   

                    
 



                                                 

                                               




                                                           
                     
                      
                           
                                                 
                                                      
                                 
 
                                                                               
                                                                  

                                                                  
 



                                              

                                                          
 
                                                        
                                                  

 

                                                  
 
                           

                                                



                 






                                                                                                       



                                                                          
                                           

      
                                                 





                                              
                        






















                                                                                     
                                                                              









                                                              
                                                                               


                        
                                              
 
                                              





                                                         
                                                

                  

                                   
                     

                            
                       
                              
                   
                    
 

                                      

                                               

                      
                                                     
 






                                                                   
                                                    
                              
 
                                                           




                                                   
                                                   
                                                                     
                                                                               


                                                                                   
           
                                                             

                            
 


                                               
                                                    

                                   
 
                               
                        
 





                                                                       
                                                              
                       
                                              
                                                                               



                                                

      

                             
                                                                       




                                                                             
                                                     
      
                                                                       

                        
                                                     

                                                      

                                                                                 



                                      
                                         
                                        



                                          
                                                                
                                       
 
                                                        
                                              
 
                                    

                                                 
                                  





                                                                     




              
                                                
 
                                 
                                                         


                                 


         
                                                                                           
 
                                 
                                  
                                                                  


         
                                                                                           
 
                                 
                                  
                                                                  


         
                                                                                      
 
                                 

                                    
                                             


           
                                                                          
 
                                              
                                             
                                                                     


           
                                                                    
 
                                              
                                             
                                                                     


           
                                                                                 
 
                                         
                                                   



                                                    


                                                                            

                                





                                                                             
                                                               

 

                                       

                                         












                                                                
         
                                                                                                                                                                    
 

                                         
 
                                 


                                   
                                              
                                  
                                                             


                                          
                                           


                                                                        
                                           







                                                                           


          
                                       


         
                                                                                                                                      
 

                                                         
 
                                 





                                         
                                                     




















                                                                        





                                             
               
                                       


         
                                                                                                                                                    
 

                                           
 
                                 
                                  
                                                                    


                                    
                                                               







                                                                 
                                                                                                                     





                                                                 
                                                                                                                                                    
 

                                                           
 
                                 
                                  
                                                                    


                                    
                                      

















                                                               
                                                      


           
                                                                                                                          
 

                                            



                                    
                                      






                                                                  
                                      
                                                       


    
                                                                                                                         
 


                                                                    
 
                                                                  


    
                                                                                             
 
                                 





                                                                        
                                                                               
 
                                 





                                                                 
                                                                                             







                                                                        
                                                                               





                                                                 






                                                          
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "eldbus_private.h"
#include "eldbus_private_types.h"

#define ELDBUS_CONNECTION_CHECK(conn)                        \
  do                                                        \
    {                                                       \
       EINA_SAFETY_ON_NULL_RETURN(conn);                    \
       if (!EINA_MAGIC_CHECK(conn, ELDBUS_CONNECTION_MAGIC)) \
         {                                                  \
            EINA_MAGIC_FAIL(conn, ELDBUS_CONNECTION_MAGIC);  \
            return;                                         \
         }                                                  \
       EINA_SAFETY_ON_TRUE_RETURN(conn->refcount <= 0);     \
    }                                                       \
  while (0)

#define ELDBUS_CONNECTION_CHECK_RETVAL(conn, retval)                 \
  do                                                                \
    {                                                               \
       EINA_SAFETY_ON_NULL_RETURN_VAL(conn, retval);                \
       if (!EINA_MAGIC_CHECK(conn, ELDBUS_CONNECTION_MAGIC))         \
         {                                                          \
            EINA_MAGIC_FAIL(conn, ELDBUS_CONNECTION_MAGIC);          \
            return retval;                                          \
         }                                                          \
       EINA_SAFETY_ON_TRUE_RETURN_VAL(conn->refcount <= 0, retval); \
    }                                                               \
  while (0)

typedef struct _Eldbus_Connection_Context_Event_Cb
{
   EINA_INLIST;
   Eldbus_Connection_Event_Cb cb;
   const void               *cb_data;
   Eina_Bool                 deleted : 1;
} Eldbus_Connection_Context_Event_Cb;

typedef struct _Eldbus_Connection_Context_NOC_Cb
{
   EINA_INLIST;
   Eldbus_Name_Owner_Changed_Cb cb;
   const void                 *cb_data;
   Eina_Bool                   deleted : 1;
   Ecore_Idler                *idler;
   Eina_Bool                   allow_initial : 1;
} Eldbus_Connection_Context_NOC_Cb;

typedef struct _Eldbus_Handler_Data
{
   EINA_INLIST;
   int               fd;
   Ecore_Fd_Handler *fd_handler;
   Eldbus_Connection *conn;
   DBusWatch        *watch;
   int               enabled;
} Eldbus_Handler_Data;

typedef struct _Eldbus_Timeout_Data
{
   EINA_INLIST;
   Ecore_Timer      *handler;
   DBusTimeout      *timeout;
   Eldbus_Connection *conn;
   int               interval;
} Eldbus_Timeout_Data;

static const Eldbus_Version _version = {VMAJ, VMIN, VMIC, VREV};
EAPI const Eldbus_Version * eldbus_version = &_version;

static int _eldbus_init_count = 0;
int _eldbus_log_dom = -1;
int eldbus_model_log_dom = -1;

/* We don't save ELDBUS_CONNECTION_TYPE_UNKNOWN in here so we need room for 
 * last - 1 elements */
static void *shared_connections[ELDBUS_CONNECTION_TYPE_LAST - 1];
static Eina_Hash *address_connections = NULL;

static void _eldbus_connection_event_callback_call(Eldbus_Connection *conn, Eldbus_Connection_Event_Type type, const void *event_info);
static void _eldbus_connection_context_event_cb_del(Eldbus_Connection_Context_Event *ce, Eldbus_Connection_Context_Event_Cb *ctx);
static void eldbus_dispatch_name_owner_change(Eldbus_Connection_Name *cn, const char *old_id);
static void _eldbus_connection_free(Eldbus_Connection *conn);

EAPI int
eldbus_init(void)
{
   if (_eldbus_init_count++ > 0)
     return _eldbus_init_count;

   if (!eina_init())
     {
        fputs("Eldbus: Unable to initialize eina\n", stderr);
        return 0;
     }

   _eldbus_log_dom = eina_log_domain_register("eldbus", EINA_COLOR_BLUE);
   if (_eldbus_log_dom < 0)
     {
        EINA_LOG_ERR("Unable to create an 'eldbus' log domain");
        eina_shutdown();
        return 0;
     }

   eldbus_model_log_dom = eina_log_domain_register("eldbus_model", EINA_COLOR_CYAN);
   if (eldbus_model_log_dom < 0)
     {
        EINA_LOG_ERR("Unable to create an 'eldbus_model' log domain");
        eina_log_domain_unregister(_eldbus_log_dom);
        _eldbus_log_dom = -1;
        eina_shutdown();
        return 0;
     }

   eina_magic_string_set(ELDBUS_CONNECTION_MAGIC, "Eldbus_Connection");
   eina_magic_string_set(ELDBUS_MESSAGE_MAGIC, "Eldbus_Message");
   eina_magic_string_set(ELDBUS_SIGNAL_HANDLER_MAGIC, "Eldbus_Signal_Handler");
   eina_magic_string_set(ELDBUS_PENDING_MAGIC, "Eldbus_Pending");
   eina_magic_string_set(ELDBUS_OBJECT_MAGIC, "Eldbus_Object");
   eina_magic_string_set(ELDBUS_PROXY_MAGIC, "Eldbus_Proxy");
   eina_magic_string_set(ELDBUS_MESSAGE_ITERATOR_MAGIC, "Eldbus_Message_Iterator");
   eina_magic_string_set(ELDBUS_SERVICE_INTERFACE_MAGIC, "Eldbus_Service_Interface");


   if (!eldbus_message_init()) goto message_failed;
   if (!eldbus_signal_handler_init()) goto signal_handler_failed;
   if (!eldbus_pending_init()) goto pending_failed;
   if (!eldbus_object_init()) goto object_failed;
   if (!eldbus_proxy_init()) goto proxy_failed;
   if (!eldbus_service_init()) goto service_failed;

   return _eldbus_init_count;

service_failed:
   eldbus_proxy_shutdown();
proxy_failed:
   eldbus_object_shutdown();
object_failed:
   eldbus_pending_shutdown();
pending_failed:
   eldbus_signal_handler_shutdown();
signal_handler_failed:
   eldbus_message_shutdown();
message_failed:
   eina_log_domain_unregister(eldbus_model_log_dom);
   eldbus_model_log_dom = -1;
   eina_log_domain_unregister(_eldbus_log_dom);
   _eldbus_log_dom = -1;
   eina_shutdown();

   return 0;
}

static void
print_live_connection(Eldbus_Connection *conn)
{
   if (!conn->names)
     ERR("conn=%p has no alive objects", conn);
   else
     {
        Eina_Iterator *iter = eina_hash_iterator_data_new(conn->names);
        Eldbus_Connection_Name *name;
        EINA_ITERATOR_FOREACH(iter, name)
          {
             Eldbus_Object *obj;
             Eina_Iterator *inner_itr;
             if (!name->objects) continue;

             inner_itr = eina_hash_iterator_data_new(name->objects);
             EINA_ITERATOR_FOREACH(inner_itr, obj)
                ERR("conn=%p alive object=%p %s of bus=%s", conn, obj,
                    obj->name, name->name);
             eina_iterator_free(inner_itr);
          }
        eina_iterator_free(iter);
     }

   if (!conn->pendings)
     ERR("conn=%p has no alive pending calls", conn);
   else
     {
        Eldbus_Pending *p;
        EINA_INLIST_FOREACH(conn->pendings, p)
          ERR("conn=%p alive pending call=%p dest=%s path=%s %s.%s()",
              conn, p,
              eldbus_pending_destination_get(p),
              eldbus_pending_path_get(p),
              eldbus_pending_interface_get(p),
              eldbus_pending_method_get(p));
     }
}

EAPI int
eldbus_shutdown(void)
{
   if (_eldbus_init_count <= 0)
     {
        ERR("Init count not greater than 0 in shutdown.");
        _eldbus_init_count = 0;
        return 0;
     }
   if (--_eldbus_init_count)
     return _eldbus_init_count;

   if (shared_connections[ELDBUS_CONNECTION_TYPE_SESSION - 1])
     {
        CRI("Alive TYPE_SESSION connection");
        print_live_connection(shared_connections[ELDBUS_CONNECTION_TYPE_SESSION - 1]);
     }
   if (shared_connections[ELDBUS_CONNECTION_TYPE_SYSTEM - 1])
     {
        CRI("Alive TYPE_SYSTEM connection");
        print_live_connection(shared_connections[ELDBUS_CONNECTION_TYPE_SYSTEM - 1]);
     }
   if (shared_connections[ELDBUS_CONNECTION_TYPE_STARTER - 1])
     {
        CRI("Alive TYPE_STARTER connection");
        print_live_connection(shared_connections[ELDBUS_CONNECTION_TYPE_STARTER - 1]);
     }
   if (shared_connections[ELDBUS_CONNECTION_TYPE_ADDRESS - 1])
     {
        if (eina_hash_population(address_connections))
          {
             Eina_Iterator *it;
             Eina_Hash_Tuple *tuple;

             it = eina_hash_iterator_tuple_new(address_connections);
             EINA_ITERATOR_FOREACH(it, tuple)
               {
                  CRI("Alive TYPE_ADDRESS connection: %s", (char*)tuple->key);
                  print_live_connection(tuple->data);
               }
             eina_iterator_free(it);
          }

        eina_hash_free(address_connections);
        address_connections = shared_connections[ELDBUS_CONNECTION_TYPE_ADDRESS - 1] = NULL;
     }

   eldbus_service_shutdown();
   eldbus_proxy_shutdown();
   eldbus_object_shutdown();
   eldbus_pending_shutdown();
   eldbus_signal_handler_shutdown();
   eldbus_message_shutdown();

   eina_log_domain_unregister(eldbus_model_log_dom);
   eldbus_model_log_dom = -1;
   eina_log_domain_unregister(_eldbus_log_dom);
   _eldbus_log_dom = -1;
   eina_shutdown();

   return 0;
}

/* TODO: mempool of Eldbus_Context_Free_Cb */
typedef struct _Eldbus_Context_Free_Cb
{
   EINA_INLIST;
   Eldbus_Free_Cb cb;
   const void   *data;
} Eldbus_Context_Free_Cb;

void
eldbus_cbs_free_dispatch(Eina_Inlist **p_lst, const void *dead_pointer)
{
   Eina_Inlist *lst = *p_lst;
   *p_lst = NULL;
   while (lst)
     {
        Eina_Inlist *next = lst->next;
        Eldbus_Context_Free_Cb *ctx;

        ctx = EINA_INLIST_CONTAINER_GET(lst, Eldbus_Context_Free_Cb);
        ctx->cb((void *)ctx->data, dead_pointer);
        free(ctx);

        lst = next;
     }
}

Eina_Inlist *
eldbus_cbs_free_add(Eina_Inlist *lst, Eldbus_Free_Cb cb, const void *data)
{
   Eldbus_Context_Free_Cb *ctx = malloc(sizeof(Eldbus_Context_Free_Cb));
   EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, lst);

   ctx->cb = cb;
   ctx->data = data;

   return eina_inlist_append(lst, EINA_INLIST_GET(ctx));
}

Eina_Inlist *
eldbus_cbs_free_del(Eina_Inlist *lst, Eldbus_Free_Cb cb, const void *data)
{
   Eldbus_Context_Free_Cb *ctx;

   EINA_INLIST_FOREACH(lst, ctx)
     {
        if (ctx->cb != cb) continue;
        if ((data) && (ctx->data != data)) continue;

        lst = eina_inlist_remove(lst, EINA_INLIST_GET(ctx));
        free(ctx);
        return lst;
     }

   ERR("Couldn't find cb_free=%p data=%p", cb, data);
   return lst;
}

typedef struct _Eldbus_Data
{
   EINA_INLIST;
   const void  *data;
   unsigned int keylen;
   char         key[];
} Eldbus_Data;

static inline Eldbus_Data *
eldbus_data_find(Eina_Inlist **p_lst, const char *key)
{
   unsigned int keylen = strlen(key);
   Eldbus_Data *d;

   EINA_INLIST_FOREACH(*p_lst, d)
     {
        if ((keylen == d->keylen) && (memcmp(key, d->key, keylen) == 0))
          {
             *p_lst = eina_inlist_promote(*p_lst, EINA_INLIST_GET(d));
             return d;
          }
     }

   return NULL;
}

void
eldbus_data_set(Eina_Inlist **p_lst, const char *key, const void *data)
{
   Eldbus_Data *d = eldbus_data_find(p_lst, key);
   unsigned int keylen = strlen(key);

   if (d)
     {
        *p_lst = eina_inlist_remove(*p_lst, EINA_INLIST_GET(d));
        free(d);
     }

   d = malloc(sizeof(Eldbus_Data) + keylen + 1);
   EINA_SAFETY_ON_NULL_RETURN(d);

   d->data = data;
   d->keylen = keylen;
   memcpy(d->key, key, keylen + 1);

   *p_lst = eina_inlist_prepend(*p_lst, EINA_INLIST_GET(d));
}

void *
eldbus_data_get(Eina_Inlist **p_lst, const char *key)
{
   Eldbus_Data *d = eldbus_data_find(p_lst, key);
   return d ? (void *)d->data : NULL;
}

void *
eldbus_data_del(Eina_Inlist **p_lst, const char *key)
{
   Eldbus_Data *d = eldbus_data_find(p_lst, key);
   void *ret;
   if (!d) return NULL;

   ret = (void *)d->data;
   *p_lst = eina_inlist_remove(*p_lst, EINA_INLIST_GET(d));
   free(d);

   return ret;
}

void
eldbus_data_del_all(Eina_Inlist **p_list)
{
   Eina_Inlist *n = *p_list;
   *p_list = NULL;

   while (n)
     {
        Eldbus_Data *d = EINA_INLIST_CONTAINER_GET(n, Eldbus_Data);
        n = eina_inlist_remove(n, n);
        DBG("key=%s, data=%p", d->key, d->data);
        free(d);
     }
}

static void
eldbus_connection_name_gc(Eldbus_Connection *conn, Eldbus_Connection_Name *cn)
{
   if (cn->refcount > 0)
     return;
   if (cn->objects && eina_hash_population(cn->objects) > 0)
     return;
   if (cn->event_handlers.list != NULL)
     return;

   eina_hash_del(conn->names, cn->name, cn);
   if (cn->name_owner_changed)
     eldbus_signal_handler_del(cn->name_owner_changed);
   if (cn->objects)
     eina_hash_free(cn->objects);
   eina_stringshare_del(cn->name);
   if (cn->name_owner_get)
     eldbus_pending_cancel(cn->name_owner_get);
   if (cn->unique_id)
     eina_stringshare_del(cn->unique_id);
   eina_list_free(cn->event_handlers.to_delete);
   free(cn);
}

void
eldbus_connection_name_object_del(Eldbus_Connection *conn, const Eldbus_Object *obj)
{
   Eldbus_Connection_Name *cn = eina_hash_find(conn->names, obj->name);

   if (!cn) return;
   if (!cn->objects) return;
   eina_hash_del(cn->objects, obj->path, obj);

   eldbus_connection_name_gc(conn, cn);
}

void
eldbus_connection_name_object_set(Eldbus_Connection *conn, Eldbus_Object *obj)
{
   Eldbus_Connection_Name *cn;

   cn = eldbus_connection_name_get(conn, obj->name);
   eina_hash_add(cn->objects, obj->path, obj);

   return;
}

static void
on_name_owner_changed(void *data, const Eldbus_Message *msg)
{
   const char *bus, *older_id, *new_id;
   Eldbus_Connection_Name *cn = data;

   if (!eldbus_message_arguments_get(msg, "sss", &bus, &older_id, &new_id))
     {
        ERR("Error getting arguments from NameOwnerChanged cn=%s", cn->name);
        return;
     }

   eina_stringshare_replace(&cn->unique_id, new_id);
   eldbus_dispatch_name_owner_change(cn, older_id);
}

static void
on_get_name_owner(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED)
{
   const char *unique_id = "", *error_name;
   Eldbus_Connection_Name *cn = data;

   cn->name_owner_get = NULL;

   if (eldbus_message_error_get(msg, &error_name, NULL))
     {
        if (!strcmp(error_name, ELDBUS_ERROR_PENDING_CANCELED))
          return;
        DBG("GetNameOwner of bus = %s returned an error", cn->name);
     }
   else if (!eldbus_message_arguments_get(msg, "s", &unique_id))
     ERR("Error getting arguments from GetNameOwner");

   cn->unique_id = eina_stringshare_add(unique_id);
   eldbus_dispatch_name_owner_change(cn, NULL);
}

void
eldbus_connection_name_ref(Eldbus_Connection_Name *cn)
{
   EINA_SAFETY_ON_NULL_RETURN(cn);
   cn->refcount++;
}

void
eldbus_connection_name_unref(Eldbus_Connection *conn, Eldbus_Connection_Name *cn)
{
   ELDBUS_CONNECTION_CHECK(conn);
   EINA_SAFETY_ON_NULL_RETURN(cn);

   cn->refcount--;
   eldbus_connection_name_gc(conn, cn);
}

Eldbus_Connection_Name *
eldbus_connection_name_get(Eldbus_Connection *conn, const char *name)
{
   Eldbus_Connection_Name *cn;
   ELDBUS_CONNECTION_CHECK_RETVAL(conn, NULL);

   cn = eina_hash_find(conn->names, name);
   if (cn) return cn;

   cn = calloc(1, sizeof(Eldbus_Connection_Name));
   EINA_SAFETY_ON_NULL_RETURN_VAL(cn, NULL);
   cn->name = eina_stringshare_add(name);
   cn->objects = eina_hash_string_superfast_new(NULL);

   if (!strcmp(name, ELDBUS_FDO_BUS))
     {
        cn->unique_id = eina_stringshare_add(name);
        goto end;
     }

   if (name[0] == ':')
     cn->unique_id = eina_stringshare_add(name);
   else
     cn->name_owner_get = eldbus_name_owner_get(conn, cn->name, on_get_name_owner, cn);

   cn->name_owner_changed = _eldbus_signal_handler_add(conn, ELDBUS_FDO_BUS,
                                                      ELDBUS_FDO_PATH,
                                                      ELDBUS_FDO_INTERFACE,
                                                      "NameOwnerChanged",
                                                      on_name_owner_changed, cn);
   eldbus_signal_handler_match_extra_set(cn->name_owner_changed, "arg0",
                                        cn->name, NULL);

end:
   eina_hash_direct_add(conn->names, cn->name, cn);
   return cn;
}

Eldbus_Object *
eldbus_connection_name_object_get(Eldbus_Connection *conn, const char *name, const char *path)
{
   Eldbus_Connection_Name *cn;

   ELDBUS_CONNECTION_CHECK_RETVAL(conn, NULL);
   cn = eina_hash_find(conn->names, name);
   if (!cn) return NULL;
   if (!cn->objects) return NULL;
   return eina_hash_find(cn->objects, path);
}


static void
eldbus_fd_handler_del(Eldbus_Handler_Data *hd)
{
   if (!hd->fd_handler) return;

   DBG("free Eldbus_Handler_Data %d", hd->fd);
   hd->conn->fd_handlers = eina_inlist_remove(hd->conn->fd_handlers,
                                              EINA_INLIST_GET(hd));
   if (hd->fd_handler)
     {
        ecore_main_fd_handler_del(hd->fd_handler);
        hd->fd_handler = NULL;
     }

   free(hd);
}

static Eina_Bool
eldbus_fd_handler(void *data, Ecore_Fd_Handler *fd_handler)
{
   Eldbus_Handler_Data *hd = data;
   unsigned int condition = 0;

   DBG("Got Ecore_Fd_Handle@%p", fd_handler);

   if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ)) condition |= DBUS_WATCH_READABLE;
   if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_WRITE)) condition |= DBUS_WATCH_WRITABLE;
   if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_ERROR)) condition |= DBUS_WATCH_ERROR;

   DBG("dbus connection@%p fdh=%d flags: [%s%s%s]", hd->conn, hd->fd,
       (condition & DBUS_WATCH_READABLE) ? "read " : "",
       (condition & DBUS_WATCH_WRITABLE) ? "write " : "",
       (condition & DBUS_WATCH_ERROR) ? "error" : "");

   dbus_watch_handle(hd->watch, condition);

   return ECORE_CALLBACK_RENEW;
}

static void
eldbus_fd_handler_add(Eldbus_Handler_Data *hd)
{
   unsigned int dflags;
   Ecore_Fd_Handler_Flags eflags;

   if (hd->fd_handler) return;
   dflags = dbus_watch_get_flags(hd->watch);
   eflags = ECORE_FD_ERROR;
   if (dflags & DBUS_WATCH_READABLE) eflags |= ECORE_FD_READ;
   if (dflags & DBUS_WATCH_WRITABLE) eflags |= ECORE_FD_WRITE;

   DBG("Watching fd %d with flags: [%s%serror]", hd->fd,
      (eflags & ECORE_FD_READ) ? "read " : "",
      (eflags & ECORE_FD_WRITE) ? "write " : "");

   hd->fd_handler = ecore_main_fd_handler_add(hd->fd,
                                              eflags,
                                              eldbus_fd_handler,
                                              hd,
                                              NULL,
                                              NULL);
}

static void
eldbus_handler_data_free(void *data)
{
   Eldbus_Handler_Data *hd = data;
   eldbus_fd_handler_del(hd);
}

static dbus_bool_t
cb_watch_add(DBusWatch *watch, void *data)
{
   Eldbus_Connection *conn = data;
   Eldbus_Handler_Data *hd;

   if (!dbus_watch_get_enabled(watch)) return EINA_TRUE;

   DBG("cb_watch_add (enabled: %d)", dbus_watch_get_unix_fd(watch));

   hd = calloc(1, sizeof(Eldbus_Handler_Data));
   EINA_SAFETY_ON_NULL_RETURN_VAL(hd, EINA_FALSE);
   dbus_watch_set_data(watch, hd, eldbus_handler_data_free);
   hd->conn = conn;
   hd->watch = watch;
   hd->enabled = dbus_watch_get_enabled(watch);
   hd->fd = dbus_watch_get_unix_fd(hd->watch);

   conn->fd_handlers = eina_inlist_append(hd->conn->fd_handlers,
                                          EINA_INLIST_GET(hd));
   eldbus_fd_handler_add(hd);

   return EINA_TRUE;
}

static void
cb_watch_del(DBusWatch *watch, void *data EINA_UNUSED)
{
   DBG("cb_watch_del");
   /* will trigger eldbus_handler_data_free() */
   dbus_watch_set_data(watch, NULL, NULL);
}

static void
cb_watch_toggle(DBusWatch *watch, void *data EINA_UNUSED)
{
   Eldbus_Handler_Data *hd;
   hd = dbus_watch_get_data(watch);
   if (!hd) return;
   DBG("cb_watch_toggle %d", hd->fd);

   hd->enabled = dbus_watch_get_enabled(watch);

   DBG("watch %p is %sabled", hd, hd->enabled ? "en" : "dis");
   if (hd->enabled)
     {
        eldbus_fd_handler_add(hd);
     }
   else
     {
        ecore_main_fd_handler_del(hd->fd_handler);
        hd->fd_handler = NULL;
     }
}

static void
eldbus_timeout_data_free(void *timeout_data)
{
   Eldbus_Timeout_Data *td = timeout_data;
   td->conn->timeouts = eina_inlist_remove(td->conn->timeouts,
                                           EINA_INLIST_GET(td));
   DBG("Timeout -- freeing timeout_data %p", td);
   if (td->handler) ecore_timer_del(td->handler);
   free(td);
}

static Eina_Bool
eldbus_timeout_handler(void *data)
{
   Eldbus_Timeout_Data *td = data;
   td->handler = NULL;

   if (!dbus_timeout_get_enabled(td->timeout))
     {
        DBG("timeout_handler (not enabled, ending)");
        return ECORE_CALLBACK_CANCEL;
     }

   DBG("Telling dbus to handle timeout with data %p", data);
   dbus_timeout_handle(td->timeout);
   return ECORE_CALLBACK_CANCEL;
}

static dbus_bool_t
cb_timeout_add(DBusTimeout *timeout, void *data)
{
   Eldbus_Connection *conn = data;
   Eldbus_Timeout_Data *td;

   if (!dbus_timeout_get_enabled(timeout))
     return EINA_TRUE;

   DBG("Adding timeout for connection@%p", conn);
   td = calloc(1, sizeof(Eldbus_Timeout_Data));
   EINA_SAFETY_ON_NULL_RETURN_VAL(td, EINA_FALSE);
   td->conn = conn;
   dbus_timeout_set_data(timeout, (void *)td, eldbus_timeout_data_free);
   td->interval = dbus_timeout_get_interval(timeout);
   td->timeout = timeout;

   td->handler = ecore_timer_add(td->interval / 1000.0, eldbus_timeout_handler, td);
   conn->timeouts = eina_inlist_append(conn->timeouts,
                                       EINA_INLIST_GET(td));

   return EINA_TRUE;
}

static void
cb_timeout_del(DBusTimeout *timeout, void *data EINA_UNUSED)
{
   DBG("timeout del!");
   /* will trigger eldbus_timeout_data_free() */
   dbus_timeout_set_data(timeout, NULL, NULL);
}

static void
cb_timeout_toggle(DBusTimeout *timeout, void *data EINA_UNUSED)
{
   Eldbus_Timeout_Data *td;

   td = dbus_timeout_get_data(timeout);

   DBG("Timeout toggle; data@%p", td);
   if (dbus_timeout_get_enabled(td->timeout))
     {
        td->interval = dbus_timeout_get_interval(timeout);
        td->handler
          = ecore_timer_add(td->interval, eldbus_timeout_handler, td);

        DBG("Timeout is enabled with interval %d, timer@%p",
           td->interval, td->handler);
     }
   else
     {
        DBG("Timeout is disabled, destroying timer@%p", td->handler);
        ecore_timer_del(td->handler);
        td->handler = NULL;
     }
}

static Eina_Bool
eldbus_idler(void *data)
{
   Eldbus_Connection *conn = data;

   DBG("Connection@%p: Dispatch status: %d", conn,
       dbus_connection_get_dispatch_status(conn->dbus_conn));

   if (dbus_connection_get_dispatch_status(conn->dbus_conn) ==
       DBUS_DISPATCH_COMPLETE)
     {
        DBG("Connection@%p: Dispatch complete, idler@%p finishing",
            conn, conn->idler);
        conn->idler = NULL;
        return ECORE_CALLBACK_CANCEL;
     }
   DBG("Connection@%p: Dispatching", conn);
   eldbus_init();
   eldbus_connection_ref(conn);
   dbus_connection_dispatch(conn->dbus_conn);
   eldbus_connection_unref(conn);
   eldbus_shutdown();
   return ECORE_CALLBACK_RENEW;
}

static void
cb_dispatch_status(DBusConnection *dbus_conn EINA_UNUSED, DBusDispatchStatus new_status, void *data)
{
   Eldbus_Connection *conn = data;

   if (!conn->refcount)
     {
        DBG("Connection[%p] being freed, dispatch blocked", conn);
        return;
     }

   DBG("Connection@%p: Dispatch status: %d", conn, new_status);

   if ((new_status == DBUS_DISPATCH_DATA_REMAINS) && (!conn->idler))
     {
        conn->idler = ecore_idler_add(eldbus_idler, conn);
        DBG("Connection@%p: Adding idler@%p to handle remaining dispatch data",
            conn, conn->idler);
     }
   else if ((new_status != DBUS_DISPATCH_DATA_REMAINS) && (conn->idler))
     {
        DBG("Connection@%p: No remaining dispatch data, clearing idler@%p",
            conn, conn->idler);

        ecore_idler_del(conn->idler);
        conn->idler = NULL;
     }
}

static inline Eina_Bool
extra_arguments_check(DBusMessage *msg, Eldbus_Signal_Handler *sh)
{
   DBusMessageIter iter;
   Signal_Argument *arg;
   unsigned int arg_index = 0;

   dbus_message_iter_init(msg, &iter);
   EINA_INLIST_FOREACH(sh->args, arg)
     {
        const char *arg_msg;
        int type = 0;

        while((arg->index > arg_index) && dbus_message_iter_next(&iter))
          arg_index++;

        if (arg_index != arg->index)
          return EINA_FALSE;

        type = dbus_message_iter_get_arg_type(&iter);
        if (!(type == 's' || type == 'o'))
          return EINA_FALSE;

        dbus_message_iter_get_basic(&iter, &arg_msg);
        if (strcmp(arg_msg, arg->value))
          return EINA_FALSE;
     }
   return EINA_TRUE;
}

static void
cb_signal_dispatcher(Eldbus_Connection *conn, DBusMessage *msg)
{
   Eldbus_Message *eldbus_msg;
   Eina_Inlist *next;

   eldbus_msg = eldbus_message_new(EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN(eldbus_msg);

   eldbus_msg->dbus_msg = dbus_message_ref(msg);
   dbus_message_iter_init(eldbus_msg->dbus_msg,
                          &eldbus_msg->iterator->dbus_iterator);

   eldbus_connection_ref(conn);
   eldbus_init();
   /*
    * Do the walking open-coded so we don't crash if a callback
    * removes other signal handlers from the list and we don't own
    * yet a reference to them.
    */
   next = conn->signal_handlers;
   while (next != NULL)
     {
        Eldbus_Signal_Handler *sh;

        sh = EINA_INLIST_CONTAINER_GET(next, Eldbus_Signal_Handler);
        next = next->next;

        if (sh->dangling) continue;
        if (sh->sender)
          {
             if (sh->bus)
               {
                  if ((sh->bus->unique_id == NULL) ||
                      (!sh->bus->unique_id[0]) ||
                      (!dbus_message_has_sender(msg, sh->bus->unique_id)))
                    continue;
               }
             else
               if (!dbus_message_has_sender(msg, sh->sender)) continue;
          }
        if (sh->path && !dbus_message_has_path(msg, sh->path)) continue;
        if (sh->interface && !dbus_message_has_interface(msg, sh->interface))
          continue;
        if (sh->member && !dbus_message_has_member(msg, sh->member)) continue;
        if (!extra_arguments_check(msg, sh)) continue;

        eldbus_signal_handler_ref(sh);
        sh->cb((void *)sh->cb_data, eldbus_msg);
        /* update next signal handler because the list may have changed */
        next = EINA_INLIST_GET(sh)->next;
        eldbus_signal_handler_unref(sh);

        /*
         * Rewind iterator so another signal handler matching the same signal
         * can iterate over it.
         */
        dbus_message_iter_init(eldbus_msg->dbus_msg,
                               &eldbus_msg->iterator->dbus_iterator);
     }

   eldbus_message_unref(eldbus_msg);
   eldbus_connection_unref(conn);
   eldbus_shutdown();
}

static DBusHandlerResult
eldbus_filter(DBusConnection *conn_dbus EINA_UNUSED, DBusMessage *message, void *user_data)
{
   Eldbus_Connection *conn = user_data;

   DBG("Connection@%p Got message:\n"
       "  Type: %s\n"
       "  Path: %s\n"
       "  Interface: %s\n"
       "  Member: %s\n"
       "  Sender: %s", conn,
       dbus_message_type_to_string(dbus_message_get_type(message)),
       dbus_message_get_path(message),
       dbus_message_get_interface(message),
       dbus_message_get_member(message),
       dbus_message_get_sender(message));

   switch (dbus_message_get_type(message))
     {
      case DBUS_MESSAGE_TYPE_METHOD_CALL:
        DBG("  Signature: %s", dbus_message_get_signature(message));
        break;
      case DBUS_MESSAGE_TYPE_METHOD_RETURN:
      case DBUS_MESSAGE_TYPE_ERROR:
        DBG("  Reply serial: %d", dbus_message_get_reply_serial(message));
        break;
      case DBUS_MESSAGE_TYPE_SIGNAL:
        DBG("  Signature: %s", dbus_message_get_signature(message));
        cb_signal_dispatcher(conn, message);
        break;
     }

   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

static void
eldbus_connection_setup(Eldbus_Connection *conn)
{
   DBG("Setting up connection %p", conn);

   /* connection_setup */
   dbus_connection_set_exit_on_disconnect(conn->dbus_conn, EINA_FALSE);
   dbus_connection_set_watch_functions(conn->dbus_conn,
                                       cb_watch_add,
                                       cb_watch_del,
                                       cb_watch_toggle,
                                       conn,
                                       NULL);

   dbus_connection_set_timeout_functions(conn->dbus_conn,
                                         cb_timeout_add,
                                         cb_timeout_del,
                                         cb_timeout_toggle,
                                         conn,
                                         NULL);

   dbus_connection_set_dispatch_status_function(conn->dbus_conn,
                                                cb_dispatch_status,
                                                conn, NULL);
   dbus_connection_add_filter(conn->dbus_conn, eldbus_filter, conn, NULL);

   cb_dispatch_status(conn->dbus_conn,
                      dbus_connection_get_dispatch_status(conn->dbus_conn),
                      conn);
}

static void
_disconnected(void *data, const Eldbus_Message *msg EINA_UNUSED)
{
   Eldbus_Connection *conn = data;
   Ecore_Event_Signal_Exit *ev;

   _eldbus_connection_event_callback_call(
      conn, ELDBUS_CONNECTION_EVENT_DISCONNECTED, NULL);
   if (conn->type != ELDBUS_CONNECTION_TYPE_SESSION) return;

   ev = calloc(1, sizeof(Ecore_Event_Signal_Exit));
   if (!ev) return;

   ev->quit = EINA_TRUE;
   ecore_event_add(ECORE_EVENT_SIGNAL_EXIT, ev, NULL, NULL);
}

/* Param address is only used for ELDBUS_CONNECTION_TYPE_ADDRESS type */
static Eldbus_Connection *
_connection_get(Eldbus_Connection_Type type, const char *address, Eina_Bool shared)
{
   Eldbus_Connection *conn;
   DBusError err;
   Eldbus_Object *obj;

   EINA_SAFETY_ON_FALSE_RETURN_VAL((type < ELDBUS_CONNECTION_TYPE_LAST) &&
                                   (type > ELDBUS_CONNECTION_TYPE_UNKNOWN), NULL);
   EINA_SAFETY_ON_TRUE_RETURN_VAL((type == ELDBUS_CONNECTION_TYPE_ADDRESS) &&
                                  (address == NULL), NULL);

   conn = calloc(1, sizeof(Eldbus_Connection));
   EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL);

   dbus_error_init(&err);
   if (type == ELDBUS_CONNECTION_TYPE_ADDRESS)
      conn->dbus_conn = dbus_connection_open_private(address, &err);
   else
      conn->dbus_conn = dbus_bus_get_private(type - 1, &err);

   if (!conn->dbus_conn || dbus_error_is_set(&err))
     {
        free(conn);
        ERR("Error connecting to bus of type %d. error name: %s error message: %s",
            type, err.name, err.message);
        return NULL;
     }

   if (type == ELDBUS_CONNECTION_TYPE_ADDRESS &&
       !dbus_bus_register(conn->dbus_conn, &err))
     {
        dbus_connection_close(conn->dbus_conn);
        dbus_connection_unref(conn->dbus_conn);
        conn->dbus_conn = NULL;
        free(conn);
        ERR("Error registering with bus: %s", err.message);
        return NULL;
     }

   conn->type = type;
   conn->refcount = 1;
   conn->shared = !!shared;
   EINA_MAGIC_SET(conn, ELDBUS_CONNECTION_MAGIC);
   conn->names = eina_hash_string_superfast_new(NULL);
   eldbus_connection_setup(conn);

   eldbus_signal_handler_add(conn, NULL, DBUS_PATH_LOCAL, DBUS_INTERFACE_LOCAL,
                             "Disconnected", _disconnected, conn);
   obj = eldbus_object_get(conn, ELDBUS_FDO_BUS, ELDBUS_FDO_PATH);
   conn->fdo_proxy = eldbus_proxy_get(obj, ELDBUS_FDO_INTERFACE);

   DBG("Returned new connection at %p", conn);
   return conn;
}

EAPI Eldbus_Connection *
eldbus_private_connection_get(Eldbus_Connection_Type type)
{
   DBG("Getting private connection with type %d", type);
   return _connection_get(type, NULL, EINA_FALSE);
}

EAPI Eldbus_Connection *
eldbus_connection_get(Eldbus_Connection_Type type)
{
   Eldbus_Connection *conn;

   DBG("Getting connection with type %d", type);

   if (!type)
     return NULL;

   if (type == ELDBUS_CONNECTION_TYPE_ADDRESS)
     {
        ERR("CONNECTION_TYPE_ADDRESS must be used with appropriate address_connection_get() function");
        return NULL;
     }

   conn = (Eldbus_Connection *) shared_connections[type - 1];
   if (conn)
     {
        DBG("Connection with type %d exists at %p; reffing and returning",
            type, conn);
        return eldbus_connection_ref(conn);
     }

   conn = _connection_get(type, NULL, EINA_TRUE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL);
   shared_connections[type - 1] = conn;

   return conn;
}

EAPI Eldbus_Connection *
eldbus_address_connection_get(const char *address)
{
   Eldbus_Connection *conn = NULL;

   DBG("Getting connection with address %s", address);

   if (address_connections == NULL)
     {
        address_connections = eina_hash_string_small_new(NULL);
        shared_connections[ELDBUS_CONNECTION_TYPE_ADDRESS - 1] = address_connections;
     }
   else
     {
        conn = (Eldbus_Connection *) eina_hash_find(address_connections, address);
     }

   if (conn != NULL)
     {
        DBG("Connection with address %s exists at %p; reffing and returning",
            address, conn);
        return eldbus_connection_ref(conn);
     }

   conn = _connection_get(ELDBUS_CONNECTION_TYPE_ADDRESS, address, EINA_TRUE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL);
   eina_hash_add(address_connections, address, conn);

   return conn;
}

EAPI Eldbus_Connection *
eldbus_private_address_connection_get(const char *address)
{
   DBG("Getting private connection with address %s", address);
   return _connection_get(ELDBUS_CONNECTION_TYPE_ADDRESS, address, EINA_FALSE);
}

EAPI Eldbus_Connection *
eldbus_connection_ref(Eldbus_Connection *conn)
{
   ELDBUS_CONNECTION_CHECK_RETVAL(conn, NULL);
   DBG("conn=%p, pre-refcount=%d", conn, conn->refcount);
   conn->refcount++;
   return conn;
}

static void
_eldbus_connection_free(Eldbus_Connection *conn)
{
   unsigned int i;
   Eldbus_Handler_Data *fd_handler;
   Eldbus_Timeout_Data *timer;
   Eina_Inlist *list;
   Eldbus_Signal_Handler *h;
   Eldbus_Pending *p;
   Eina_Iterator *iter;
   Eldbus_Connection_Name *cn;
   Eina_Array *cns;
   const char *name;

   DBG("Freeing connection %p", conn);

   _eldbus_connection_event_callback_call
     (conn, ELDBUS_CONNECTION_EVENT_DEL, NULL);

   conn->refcount = 1;
   eldbus_cbs_free_dispatch(&(conn->cbs_free), conn);

   /**
    * Flush all messages in outgoing queue, also this will send all
    * ObjectManager and Property changed signals of all paths that
    * this connection is server.
    */
   dbus_connection_flush(conn->dbus_conn);

   EINA_INLIST_FOREACH_SAFE(conn->pendings, list, p)
     eldbus_pending_cancel(p);

   cns = eina_array_new(eina_hash_population(conn->names));
   iter = eina_hash_iterator_data_new(conn->names);
   EINA_ITERATOR_FOREACH(iter, cn)
     {
        while (cn->event_handlers.list)
          {
             Eldbus_Connection_Context_NOC_Cb *ctx;
             ctx = EINA_INLIST_CONTAINER_GET(cn->event_handlers.list,
                                             Eldbus_Connection_Context_NOC_Cb);
             cn->event_handlers.list = eina_inlist_remove(cn->event_handlers.list,
                                                          cn->event_handlers.list);
             free(ctx);
          }
        eina_array_push(cns, eina_stringshare_add(cn->name));
     }
   eina_iterator_free(iter);

   while ((name = eina_array_pop(cns)))
     {
        cn = eina_hash_find(conn->names, name);
        if (cn) eldbus_connection_name_gc(conn, cn);
        eina_stringshare_del(name);
     }

   eina_hash_free(conn->names);
   eina_array_free(cns);

   conn->refcount = 0;

   /* after cbs_free dispatch these shouldn't exit, error if they do */

   if (conn->pendings)
     {
        CRI("Connection %p released with live pending calls!",
                 conn);
        EINA_INLIST_FOREACH(conn->pendings, p)
          ERR("conn=%p alive pending call=%p dest=%s path=%s %s.%s()", conn, p,
              eldbus_pending_destination_get(p),
              eldbus_pending_path_get(p),
              eldbus_pending_interface_get(p),
              eldbus_pending_method_get(p));
     }

   if (conn->signal_handlers)
     {
        CRI("Connection %p released with live signal handlers!", conn);
        EINA_INLIST_FOREACH(conn->signal_handlers, h)
          ERR("conn=%p alive signal=%p %s.%s path=%s", conn, h, h->interface,
              h->member, h->path);
     }

   for (i = 0; i < ELDBUS_CONNECTION_EVENT_LAST; i++)
     {
        Eldbus_Connection_Context_Event *ce = conn->event_handlers + i;
        while (ce->list)
          {
             Eldbus_Connection_Context_Event_Cb *ctx;

             ctx = EINA_INLIST_CONTAINER_GET(ce->list,
                                             Eldbus_Connection_Context_Event_Cb);
             _eldbus_connection_context_event_cb_del(ce, ctx);
          }
        eina_list_free(ce->to_delete);
     }

   EINA_MAGIC_SET(conn, EINA_MAGIC_NONE);
   //will trigger a cb_dispatch_status()
   dbus_connection_close(conn->dbus_conn);
   dbus_connection_unref(conn->dbus_conn);
   conn->dbus_conn = NULL;

   EINA_INLIST_FOREACH_SAFE(conn->fd_handlers, list, fd_handler)
     eldbus_fd_handler_del(fd_handler);

   EINA_INLIST_FOREACH_SAFE(conn->timeouts, list, timer)
     eldbus_timeout_data_free(timer->handler);

   eldbus_data_del_all(&conn->data);

   if (conn->idler) ecore_idler_del(conn->idler);
   if (conn->type && conn->shared)
     {
        if (conn->type == ELDBUS_CONNECTION_TYPE_ADDRESS)
           eina_hash_del_by_data(address_connections, conn);
        else if (shared_connections[conn->type - 1] == (void *) conn)
           shared_connections[conn->type - 1] = NULL;
     }

   free(conn);
}

EAPI void
eldbus_connection_unref(Eldbus_Connection *conn)
{
   ELDBUS_CONNECTION_CHECK(conn);
   DBG("conn=%p, pre-refcount=%d", conn, conn->refcount);
   if (--conn->refcount > 0)
      return;
   _eldbus_connection_free(conn);
}

EAPI void
eldbus_connection_free_cb_add(Eldbus_Connection *conn, Eldbus_Free_Cb cb, const void *data)
{
   ELDBUS_CONNECTION_CHECK(conn);
   EINA_SAFETY_ON_NULL_RETURN(cb);
   conn->cbs_free = eldbus_cbs_free_add(conn->cbs_free, cb, data);
}

EAPI void
eldbus_connection_free_cb_del(Eldbus_Connection *conn, Eldbus_Free_Cb cb, const void *data)
{
   ELDBUS_CONNECTION_CHECK(conn);
   EINA_SAFETY_ON_NULL_RETURN(cb);
   conn->cbs_free = eldbus_cbs_free_del(conn->cbs_free, cb, data);
}

EAPI void
eldbus_connection_data_set(Eldbus_Connection *conn, const char *key, const void *data)
{
   ELDBUS_CONNECTION_CHECK(conn);
   EINA_SAFETY_ON_NULL_RETURN(key);
   EINA_SAFETY_ON_NULL_RETURN(data);
   eldbus_data_set(&(conn->data), key, data);
}

EAPI void *
eldbus_connection_data_get(const Eldbus_Connection *conn, const char *key)
{
   ELDBUS_CONNECTION_CHECK_RETVAL(conn, NULL);
   EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL);
   return eldbus_data_get(&(((Eldbus_Connection *)conn)->data), key);
}

EAPI void *
eldbus_connection_data_del(Eldbus_Connection *conn, const char *key)
{
   ELDBUS_CONNECTION_CHECK_RETVAL(conn, NULL);
   EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL);
   return eldbus_data_del(&(((Eldbus_Connection *)conn)->data), key);
}

static void
eldbus_dispatch_name_owner_change(Eldbus_Connection_Name *cn, const char *old_id)
{
   Eldbus_Connection_Context_NOC_Cb *ctx;
   const char *previous_id = !old_id ? "" : old_id;
   cn->event_handlers.walking++;
   EINA_INLIST_FOREACH(cn->event_handlers.list, ctx)
     {
        if (ctx->deleted) continue;
        if (!old_id && !ctx->allow_initial)
          continue;
        ctx->cb((void *)ctx->cb_data, cn->name, previous_id, cn->unique_id);
     }
   cn->event_handlers.walking--;
   EINA_LIST_FREE(cn->event_handlers.to_delete, ctx)
     {
        cn->event_handlers.list = eina_inlist_remove(cn->event_handlers.list,
                                                     EINA_INLIST_GET(ctx));
        free(ctx);
     }
   eldbus_connection_name_gc(cn->name_owner_changed->conn, cn);
}

typedef struct _dispach_name_owner_data
{
   Eldbus_Connection_Context_NOC_Cb *ctx;
   const Eldbus_Connection_Name *cn;
} dispatch_name_owner_data;

static Eina_Bool
dispach_name_owner_cb(void *context)
{
   dispatch_name_owner_data *data = context;
   data->ctx->cb((void *)data->ctx->cb_data, data->cn->name, "",
                 data->cn->unique_id);
   data->ctx->idler = NULL;
   free(data);
   return ECORE_CALLBACK_CANCEL;
}

EAPI void
eldbus_name_owner_changed_callback_add(Eldbus_Connection *conn, const char *bus, Eldbus_Name_Owner_Changed_Cb cb, const void *cb_data, Eina_Bool allow_initial_call)
{
   Eldbus_Connection_Name *cn;
   Eldbus_Connection_Context_NOC_Cb *ctx;

   ELDBUS_CONNECTION_CHECK(conn);
   EINA_SAFETY_ON_NULL_RETURN(bus);
   EINA_SAFETY_ON_NULL_RETURN(cb);

   cn = eldbus_connection_name_get(conn, bus);
   EINA_SAFETY_ON_NULL_RETURN(cn);
   ctx = calloc(1, sizeof(Eldbus_Connection_Context_NOC_Cb));
   EINA_SAFETY_ON_NULL_GOTO(ctx, cleanup);
   ctx->cb = cb;
   ctx->cb_data = cb_data;
   ctx->allow_initial = allow_initial_call;

   cn->event_handlers.list = eina_inlist_append(cn->event_handlers.list,
                                                EINA_INLIST_GET(ctx));
   if (cn->unique_id && allow_initial_call)
     {
        dispatch_name_owner_data *dispatch_data;
        dispatch_data = malloc(sizeof(dispatch_name_owner_data));
        EINA_SAFETY_ON_NULL_RETURN(dispatch_data);
        dispatch_data->cn = cn;
        dispatch_data->ctx = ctx;
        ctx->idler = ecore_idler_add(dispach_name_owner_cb, dispatch_data);
     }
   return;

cleanup:
   eldbus_connection_name_gc(conn, cn);
}

EAPI void
eldbus_name_owner_changed_callback_del(Eldbus_Connection *conn, const char *bus, Eldbus_Name_Owner_Changed_Cb cb, const void *cb_data)
{
   Eldbus_Connection_Name *cn;
   Eldbus_Connection_Context_NOC_Cb *iter, *found = NULL;

   ELDBUS_CONNECTION_CHECK(conn);
   EINA_SAFETY_ON_NULL_RETURN(bus);
   EINA_SAFETY_ON_NULL_RETURN(cb);

   cn = eina_hash_find(conn->names, bus);
   EINA_SAFETY_ON_NULL_RETURN(cn);

   EINA_INLIST_FOREACH(cn->event_handlers.list, iter)
     {
        if (cb != iter->cb) continue;
        if ((cb_data) && (cb_data != iter->cb_data)) continue;

        found = iter;
        break;
     }

   EINA_SAFETY_ON_NULL_RETURN(found);
   EINA_SAFETY_ON_TRUE_RETURN(found->deleted);

   if (cn->event_handlers.walking)
     {
        found->deleted = EINA_TRUE;
        cn->event_handlers.to_delete = eina_list_append
            (cn->event_handlers.to_delete, found);
        return;
     }

   cn->event_handlers.list = eina_inlist_remove(cn->event_handlers.list,
                                                EINA_INLIST_GET(found));
   if (found->idler)
     {
        dispatch_name_owner_data *data;
        data = ecore_idler_del(found->idler);
        free(data);
     }
   free(found);
   eldbus_connection_name_gc(conn, cn);
}

EAPI void
eldbus_connection_event_callback_add(Eldbus_Connection *conn, Eldbus_Connection_Event_Type type, Eldbus_Connection_Event_Cb cb, const void *cb_data)
{
   Eldbus_Connection_Context_Event *ce;
   Eldbus_Connection_Context_Event_Cb *ctx;

   ELDBUS_CONNECTION_CHECK(conn);
   EINA_SAFETY_ON_NULL_RETURN(cb);
   EINA_SAFETY_ON_TRUE_RETURN(type >= ELDBUS_CONNECTION_EVENT_LAST);

   ce = conn->event_handlers + type;

   ctx = calloc(1, sizeof(Eldbus_Connection_Context_Event_Cb));
   EINA_SAFETY_ON_NULL_RETURN(ctx);
   ctx->cb = cb;
   ctx->cb_data = cb_data;

   ce->list = eina_inlist_append(ce->list, EINA_INLIST_GET(ctx));
}

static void
_eldbus_connection_context_event_cb_del(Eldbus_Connection_Context_Event *ce, Eldbus_Connection_Context_Event_Cb *ctx)
{
   ce->list = eina_inlist_remove(ce->list, EINA_INLIST_GET(ctx));
   free(ctx);
}

EAPI void
eldbus_connection_event_callback_del(Eldbus_Connection *conn, Eldbus_Connection_Event_Type type, Eldbus_Connection_Event_Cb cb, const void *cb_data)
{
   Eldbus_Connection_Context_Event *ce;
   Eldbus_Connection_Context_Event_Cb *iter, *found = NULL;

   ELDBUS_CONNECTION_CHECK(conn);
   EINA_SAFETY_ON_NULL_RETURN(cb);
   EINA_SAFETY_ON_TRUE_RETURN(type >= ELDBUS_CONNECTION_EVENT_LAST);

   ce = conn->event_handlers + type;

   EINA_INLIST_FOREACH(ce->list, iter)
     {
        if (cb != iter->cb) continue;
        if ((cb_data) && (cb_data != iter->cb_data)) continue;

        found = iter;
        break;
     }

   EINA_SAFETY_ON_NULL_RETURN(found);
   EINA_SAFETY_ON_TRUE_RETURN(found->deleted);

   if (ce->walking)
     {
        found->deleted = EINA_TRUE;
        ce->to_delete = eina_list_append(ce->to_delete, found);
        return;
     }

   _eldbus_connection_context_event_cb_del(ce, found);
}

static void
_eldbus_connection_event_callback_call(Eldbus_Connection *conn, Eldbus_Connection_Event_Type type, const void *event_info)
{
   Eldbus_Connection_Context_Event *ce;
   Eldbus_Connection_Context_Event_Cb *iter;

   ce = conn->event_handlers + type;

   ce->walking++;
   EINA_INLIST_FOREACH(ce->list, iter)
     {
        if (iter->deleted) continue;
        iter->cb((void *)iter->cb_data, conn, (void *)event_info);
     }
   ce->walking--;
   if (ce->walking > 0) return;

   EINA_LIST_FREE(ce->to_delete, iter)
     _eldbus_connection_context_event_cb_del(ce, iter);
}

void
eldbus_connection_event_callback_call(Eldbus_Connection *conn, Eldbus_Connection_Event_Type type, const void *event_info)
{
   ELDBUS_CONNECTION_CHECK(conn);
   EINA_SAFETY_ON_TRUE_RETURN(type >= ELDBUS_CONNECTION_EVENT_LAST);
   EINA_SAFETY_ON_TRUE_RETURN(type == ELDBUS_CONNECTION_EVENT_DEL);

   _eldbus_connection_event_callback_call(conn, type, event_info);
}

void
eldbus_connection_signal_handler_add(Eldbus_Connection *conn, Eldbus_Signal_Handler *handler)
{
   ELDBUS_CONNECTION_CHECK(conn);
   EINA_SAFETY_ON_NULL_RETURN(handler);
   conn->signal_handlers = eina_inlist_append(conn->signal_handlers,
                                              EINA_INLIST_GET(handler));
}

void
eldbus_connection_pending_add(Eldbus_Connection *conn, Eldbus_Pending *pending)
{
   ELDBUS_CONNECTION_CHECK(conn);
   EINA_SAFETY_ON_NULL_RETURN(pending);
   conn->pendings = eina_inlist_append(conn->pendings,
                                       EINA_INLIST_GET(pending));
}

void
eldbus_connection_signal_handler_del(Eldbus_Connection *conn, Eldbus_Signal_Handler *handler)
{
   EINA_SAFETY_ON_NULL_RETURN(conn);
   EINA_SAFETY_ON_NULL_RETURN(handler);
   conn->signal_handlers = eina_inlist_remove(conn->signal_handlers,
                                              EINA_INLIST_GET(handler));
}

void
eldbus_connection_pending_del(Eldbus_Connection *conn, Eldbus_Pending *pending)
{
   EINA_SAFETY_ON_NULL_RETURN(conn);
   EINA_SAFETY_ON_NULL_RETURN(pending);
   conn->pendings = eina_inlist_remove(conn->pendings,
                                       EINA_INLIST_GET(pending));
}

const char *
eldbus_connection_unique_name_get(Eldbus_Connection *conn)
{
   ELDBUS_CONNECTION_CHECK_RETVAL(conn, NULL);
   return dbus_bus_get_unique_name(conn->dbus_conn);
}