2003-04-25 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / bus / services.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* services.c  Service management
3  *
4  * Copyright (C) 2003  Red Hat, Inc.
5  * Copyright (C) 2003  CodeFactory AB
6  *
7  * Licensed under the Academic Free License version 1.2
8  * 
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  * 
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24 #include <dbus/dbus-hash.h>
25 #include <dbus/dbus-list.h>
26 #include <dbus/dbus-mempool.h>
27
28 #include "driver.h"
29 #include "services.h"
30 #include "connection.h"
31 #include "utils.h"
32 #include "activation.h"
33 #include "policy.h"
34
35 struct BusService
36 {
37   int refcount;
38
39   BusRegistry *registry;
40   char *name;
41   DBusList *owners;
42   
43   unsigned int prohibit_replacement : 1;
44 };
45
46 struct BusRegistry
47 {
48   int refcount;
49
50   BusContext *context;
51   
52   DBusHashTable *service_hash;
53   DBusMemPool   *service_pool;
54 };
55
56 BusRegistry*
57 bus_registry_new (BusContext *context)
58 {
59   BusRegistry *registry;
60
61   registry = dbus_new0 (BusRegistry, 1);
62   if (registry == NULL)
63     return NULL;
64
65   registry->refcount = 1;
66   registry->context = context;
67   
68   registry->service_hash = _dbus_hash_table_new (DBUS_HASH_STRING,
69                                                  NULL, NULL);
70   if (registry->service_hash == NULL)
71     goto failed;
72   
73   registry->service_pool = _dbus_mem_pool_new (sizeof (BusService),
74                                                TRUE);
75   if (registry->service_pool == NULL)
76     goto failed;
77
78   return registry;
79
80  failed:
81   bus_registry_unref (registry);
82   return NULL;
83 }
84
85 void
86 bus_registry_ref (BusRegistry *registry)
87 {
88   _dbus_assert (registry->refcount > 0);
89   registry->refcount += 1;
90 }
91
92 void
93 bus_registry_unref  (BusRegistry *registry)
94 {
95   _dbus_assert (registry->refcount > 0);
96   registry->refcount -= 1;
97
98   if (registry->refcount == 0)
99     {
100       if (registry->service_hash)
101         _dbus_hash_table_unref (registry->service_hash);
102       if (registry->service_pool)
103         _dbus_mem_pool_free (registry->service_pool);
104
105       dbus_free (registry);
106     }
107 }
108
109 BusService*
110 bus_registry_lookup (BusRegistry      *registry,
111                      const DBusString *service_name)
112 {
113   BusService *service;
114
115   service = _dbus_hash_table_lookup_string (registry->service_hash,
116                                             _dbus_string_get_const_data (service_name));
117
118   return service;
119 }
120
121 BusService*
122 bus_registry_ensure (BusRegistry               *registry,
123                      const DBusString          *service_name,
124                      DBusConnection            *owner_if_created,
125                      BusTransaction            *transaction,
126                      DBusError                 *error)
127 {
128   BusService *service;
129
130   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
131   
132   _dbus_assert (owner_if_created != NULL);
133   _dbus_assert (transaction != NULL);
134
135   service = _dbus_hash_table_lookup_string (registry->service_hash,
136                                             _dbus_string_get_const_data (service_name));
137   if (service != NULL)
138     return service;
139   
140   service = _dbus_mem_pool_alloc (registry->service_pool);
141   if (service == NULL)
142     {
143       BUS_SET_OOM (error);
144       return NULL;
145     }
146
147   service->registry = registry;  
148   service->refcount = 1;
149   
150   if (!_dbus_string_copy_data (service_name, &service->name))
151     {
152       _dbus_mem_pool_dealloc (registry->service_pool, service);
153       BUS_SET_OOM (error);
154       return NULL;
155     }
156
157   if (!bus_driver_send_service_created (service->name, transaction, error))
158     {
159       bus_service_unref (service);
160       return NULL;
161     }
162
163   if (!bus_activation_service_created (bus_context_get_activation (registry->context),
164                                        service->name, transaction, error))
165     {
166       bus_service_unref (service);
167       return NULL;
168     }
169   
170   if (!bus_service_add_owner (service, owner_if_created,
171                               transaction, error))
172     {
173       bus_service_unref (service);
174       return NULL;
175     }
176   
177   if (!_dbus_hash_table_insert_string (registry->service_hash,
178                                        service->name,
179                                        service))
180     {
181       /* The add_owner gets reverted on transaction cancel */
182       BUS_SET_OOM (error);
183       return NULL;
184     }
185   
186   return service;
187 }
188
189 void
190 bus_registry_foreach (BusRegistry               *registry,
191                       BusServiceForeachFunction  function,
192                       void                      *data)
193 {
194   DBusHashIter iter;
195   
196   _dbus_hash_iter_init (registry->service_hash, &iter);
197   while (_dbus_hash_iter_next (&iter))
198     {
199       BusService *service = _dbus_hash_iter_get_value (&iter);
200
201       (* function) (service, data);
202     }
203 }
204
205 dbus_bool_t
206 bus_registry_list_services (BusRegistry *registry,
207                             char      ***listp,
208                             int         *array_len)
209 {
210   int i, j, len;
211   char **retval;
212   DBusHashIter iter;
213    
214   len = _dbus_hash_table_get_n_entries (registry->service_hash);
215   retval = dbus_new (char *, len + 1);
216
217   if (retval == NULL)
218     return FALSE;
219
220   _dbus_hash_iter_init (registry->service_hash, &iter);
221   i = 0;
222   while (_dbus_hash_iter_next (&iter))
223     {
224       BusService *service = _dbus_hash_iter_get_value (&iter);
225
226       retval[i] = _dbus_strdup (service->name);
227       if (retval[i] == NULL)
228         goto error;
229
230       i++;
231     }
232
233   retval[i] = NULL;
234   
235   if (array_len)
236     *array_len = len;
237   
238   *listp = retval;
239   return TRUE;
240   
241  error:
242   for (j = 0; j < i; j++)
243     dbus_free (retval[i]);
244   dbus_free (retval);
245
246   return FALSE;
247 }
248
249 dbus_bool_t
250 bus_registry_acquire_service (BusRegistry      *registry,
251                               DBusConnection   *connection,
252                               const DBusString *service_name,
253                               dbus_uint32_t     flags,
254                               dbus_uint32_t    *result,
255                               BusTransaction   *transaction,
256                               DBusError        *error)
257 {
258   dbus_bool_t retval;
259   DBusConnection *old_owner;
260   DBusConnection *current_owner;
261   BusClientPolicy *policy;
262   BusService *service;
263   
264   retval = FALSE;
265
266   if (_dbus_string_get_length (service_name) == 0)
267     {
268       dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
269                       "Zero-length service name is not allowed");
270       
271       _dbus_verbose ("Attempt to acquire zero-length service name\n");
272       
273       goto out;
274     }
275   
276   if (_dbus_string_get_byte (service_name, 0) == ':')
277     {
278       /* Not allowed; only base services can start with ':' */
279       dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
280                       "Cannot acquire a service starting with ':' such as \"%s\"",
281                       _dbus_string_get_const_data (service_name));
282       
283       _dbus_verbose ("Attempt to acquire invalid base service name \"%s\"",
284                      _dbus_string_get_const_data (service_name));
285       
286       goto out;
287     }
288
289   policy = bus_connection_get_policy (connection, error);
290   if (policy == NULL)
291     {
292       _DBUS_ASSERT_ERROR_IS_SET (error);
293       goto out;
294     }
295
296   if (!bus_client_policy_check_can_own (policy, connection,
297                                         service_name))
298     {
299       dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
300                       "Connection \"%s\" is not allowed to own the service \"%s\" due "
301                       "to security policies in the configuration file",
302                       bus_connection_is_active (connection) ?
303                       bus_connection_get_name (connection) :
304                       "(inactive)");
305       goto out;
306     }
307
308   if (bus_connection_get_n_services_owned (connection) >=
309       bus_context_get_max_services_per_connection (registry->context))
310     {
311       dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
312                       "Connection \"%s\" is not allowed to own more services "
313                       "(increase limits in configuration file if required)",
314                       bus_connection_is_active (connection) ?
315                       bus_connection_get_name (connection) :
316                       "(inactive)");
317       goto out;
318     }
319   
320   service = bus_registry_lookup (registry, service_name);
321
322   if (service != NULL)
323     old_owner = bus_service_get_primary_owner (service);
324   else
325     old_owner = NULL;
326       
327   if (service == NULL)
328     {
329       service = bus_registry_ensure (registry,
330                                      service_name, connection, transaction, error);
331       if (service == NULL)
332         goto out;
333     }
334
335   current_owner = bus_service_get_primary_owner (service);
336
337   if (old_owner == NULL)
338     {
339       _dbus_assert (current_owner == connection);
340
341       bus_service_set_prohibit_replacement (service,
342                                             (flags & DBUS_SERVICE_FLAG_PROHIBIT_REPLACEMENT));      
343                         
344       *result = DBUS_SERVICE_REPLY_PRIMARY_OWNER;      
345     }
346   else if (old_owner == connection)
347     *result = DBUS_SERVICE_REPLY_ALREADY_OWNER;
348   else if (!((flags & DBUS_SERVICE_FLAG_REPLACE_EXISTING)))
349     *result = DBUS_SERVICE_REPLY_SERVICE_EXISTS;
350   else if (bus_service_get_prohibit_replacement (service))
351     {
352       /* Queue the connection */
353       if (!bus_service_add_owner (service, connection,
354                                   transaction, error))
355         goto out;
356       
357       *result = DBUS_SERVICE_REPLY_IN_QUEUE;
358     }
359   else
360     {
361       /* Replace the current owner */
362
363       /* We enqueue the new owner and remove the first one because
364        * that will cause ServiceAcquired and ServiceLost messages to
365        * be sent.
366        */
367       
368       if (!bus_service_add_owner (service, connection,
369                                   transaction, error))
370         goto out;
371
372       if (!bus_service_remove_owner (service, old_owner,
373                                      transaction, error))
374         goto out;
375       
376       _dbus_assert (connection == bus_service_get_primary_owner (service));
377       *result = DBUS_SERVICE_REPLY_PRIMARY_OWNER;
378     }
379
380   retval = TRUE;
381   
382  out:
383   return retval;
384 }
385
386 static void
387 bus_service_unlink_owner (BusService      *service,
388                           DBusConnection  *owner)
389 {
390   _dbus_list_remove_last (&service->owners, owner);
391   bus_connection_remove_owned_service (owner, service);
392 }
393
394 static void
395 bus_service_unlink (BusService *service)
396 {
397   _dbus_assert (service->owners == NULL);
398
399   /* the service may not be in the hash, if
400    * the failure causing transaction cancel
401    * was in the right place, but that's OK
402    */
403   _dbus_hash_table_remove_string (service->registry->service_hash,
404                                   service->name);
405   
406   bus_service_unref (service);
407 }
408
409 static void
410 bus_service_relink (BusService           *service,
411                     DBusPreallocatedHash *preallocated)
412 {
413   _dbus_assert (service->owners == NULL);
414   _dbus_assert (preallocated != NULL);
415
416   _dbus_hash_table_insert_string_preallocated (service->registry->service_hash,
417                                                preallocated,
418                                                service->name,
419                                                service);
420   
421   bus_service_ref (service);
422 }
423
424 typedef struct
425 {
426   DBusConnection *connection;
427   BusService *service;
428 } OwnershipCancelData;
429
430 static void
431 cancel_ownership (void *data)
432 {
433   OwnershipCancelData *d = data;
434
435   /* We don't need to send messages notifying of these
436    * changes, since we're reverting something that was
437    * cancelled (effectively never really happened)
438    */
439   bus_service_unlink_owner (d->service, d->connection);
440   
441   if (d->service->owners == NULL)
442     bus_service_unlink (d->service);
443 }
444
445 static void
446 free_ownership_cancel_data (void *data)
447 {
448   OwnershipCancelData *d = data;
449
450   dbus_connection_unref (d->connection);
451   bus_service_unref (d->service);
452   
453   dbus_free (d);
454 }
455
456 static dbus_bool_t
457 add_cancel_ownership_to_transaction (BusTransaction *transaction,
458                                      BusService     *service,
459                                      DBusConnection *connection)
460 {
461   OwnershipCancelData *d;
462
463   d = dbus_new (OwnershipCancelData, 1);
464   if (d == NULL)
465     return FALSE;
466   
467   d->service = service;
468   d->connection = connection;
469
470   if (!bus_transaction_add_cancel_hook (transaction, cancel_ownership, d,
471                                         free_ownership_cancel_data))
472     {
473       dbus_free (d);
474       return FALSE;
475     }
476
477   bus_service_ref (d->service);
478   dbus_connection_ref (d->connection);
479   
480   return TRUE;
481 }
482
483 /* this function is self-cancelling if you cancel the transaction */
484 dbus_bool_t
485 bus_service_add_owner (BusService     *service,
486                        DBusConnection *owner,
487                        BusTransaction *transaction,
488                        DBusError      *error)
489 {
490   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
491   
492  /* Send service acquired message first, OOM will result
493   * in cancelling the transaction
494   */
495   if (service->owners == NULL)
496     {
497       if (!bus_driver_send_service_acquired (owner, service->name, transaction, error))
498         return FALSE;
499     }
500   
501   if (!_dbus_list_append (&service->owners,
502                           owner))
503     {
504       BUS_SET_OOM (error);
505       return FALSE;
506     }
507
508   if (!bus_connection_add_owned_service (owner, service))
509     {
510       _dbus_list_remove_last (&service->owners, owner);
511       BUS_SET_OOM (error);
512       return FALSE;
513     }
514
515   if (!add_cancel_ownership_to_transaction (transaction,
516                                             service,
517                                             owner))
518     {
519       bus_service_unlink_owner (service, owner);
520       BUS_SET_OOM (error);
521       return FALSE;
522     }
523   
524   return TRUE;
525 }
526
527 typedef struct
528 {
529   DBusConnection *connection;
530   BusService     *service;
531   DBusConnection *before_connection; /* restore to position before this connection in owners list */
532   DBusList       *connection_link;
533   DBusList       *service_link;
534   DBusPreallocatedHash *hash_entry;
535 } OwnershipRestoreData;
536
537 static void
538 restore_ownership (void *data)
539 {
540   OwnershipRestoreData *d = data;
541   DBusList *link;
542
543   _dbus_assert (d->service_link != NULL);
544   _dbus_assert (d->connection_link != NULL);
545   
546   if (d->service->owners == NULL)
547     {
548       _dbus_assert (d->hash_entry != NULL);
549       bus_service_relink (d->service, d->hash_entry);
550     }
551   else
552     {
553       _dbus_assert (d->hash_entry == NULL);
554     }
555   
556   /* We don't need to send messages notifying of these
557    * changes, since we're reverting something that was
558    * cancelled (effectively never really happened)
559    */
560   link = _dbus_list_get_first_link (&d->service->owners);
561   while (link != NULL)
562     {
563       if (link->data == d->before_connection)
564         break;
565
566       link = _dbus_list_get_next_link (&d->service->owners, link);
567     }
568   
569   _dbus_list_insert_before_link (&d->service->owners, link, d->connection_link);
570
571   /* Note that removing then restoring this changes the order in which
572    * ServiceDeleted messages are sent on destruction of the
573    * connection.  This should be OK as the only guarantee there is
574    * that the base service is destroyed last, and we never even
575    * tentatively remove the base service.
576    */
577   bus_connection_add_owned_service_link (d->connection, d->service_link);
578   
579   d->hash_entry = NULL;
580   d->service_link = NULL;
581   d->connection_link = NULL;
582 }
583
584 static void
585 free_ownership_restore_data (void *data)
586 {
587   OwnershipRestoreData *d = data;
588
589   if (d->service_link)
590     _dbus_list_free_link (d->service_link);
591   if (d->connection_link)
592     _dbus_list_free_link (d->connection_link);
593   if (d->hash_entry)
594     _dbus_hash_table_free_preallocated_entry (d->service->registry->service_hash,
595                                               d->hash_entry);
596
597   dbus_connection_unref (d->connection);
598   bus_service_unref (d->service);
599   
600   dbus_free (d);
601 }
602
603 static dbus_bool_t
604 add_restore_ownership_to_transaction (BusTransaction *transaction,
605                                       BusService     *service,
606                                       DBusConnection *connection)
607 {
608   OwnershipRestoreData *d;
609   DBusList *link;
610
611   d = dbus_new (OwnershipRestoreData, 1);
612   if (d == NULL)
613     return FALSE;
614   
615   d->service = service;
616   d->connection = connection;
617   d->service_link = _dbus_list_alloc_link (service);
618   d->connection_link = _dbus_list_alloc_link (connection);
619   d->hash_entry = _dbus_hash_table_preallocate_entry (service->registry->service_hash);
620   
621   bus_service_ref (d->service);
622   dbus_connection_ref (d->connection);
623
624   d->before_connection = NULL;
625   link = _dbus_list_get_first_link (&service->owners);
626   while (link != NULL)
627     {
628       if (link->data == connection)
629         {
630           link = _dbus_list_get_next_link (&service->owners, link);
631
632           if (link)
633             d->before_connection = link->data;
634
635           break;
636         }
637       
638       link = _dbus_list_get_next_link (&service->owners, link);
639     }
640   
641   if (d->service_link == NULL ||
642       d->connection_link == NULL ||
643       d->hash_entry == NULL ||
644       !bus_transaction_add_cancel_hook (transaction, restore_ownership, d,
645                                         free_ownership_restore_data))
646     {
647       free_ownership_restore_data (d);
648       return FALSE;
649     }
650   
651   return TRUE;
652 }
653
654 /* this function is self-cancelling if you cancel the transaction */
655 dbus_bool_t
656 bus_service_remove_owner (BusService     *service,
657                           DBusConnection *owner,
658                           BusTransaction *transaction,
659                           DBusError      *error)
660 {
661   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
662   
663   /* We send out notifications before we do any work we
664    * might have to undo if the notification-sending failed
665    */
666   
667   /* Send service lost message */
668   if (bus_service_get_primary_owner (service) == owner)
669     {
670       if (!bus_driver_send_service_lost (owner, service->name,
671                                          transaction, error))
672         return FALSE;
673     }
674
675   if (service->owners == NULL)
676     {
677       _dbus_assert_not_reached ("Tried to remove owner of a service that has no owners");
678     }
679   else if (_dbus_list_length_is_one (&service->owners))
680     {
681       if (!bus_driver_send_service_deleted (service->name,
682                                             transaction, error))
683         return FALSE;
684     }
685   else
686     {
687       DBusList *link;
688       link = _dbus_list_get_first (&service->owners);
689       _dbus_assert (link != NULL);
690       link = _dbus_list_get_next_link (&service->owners, link);
691
692       _dbus_assert (link != NULL);
693
694       /* This will be our new owner */
695       if (!bus_driver_send_service_acquired (link->data,
696                                              service->name,
697                                              transaction,
698                                              error))
699         return FALSE;
700     }
701
702   if (!add_restore_ownership_to_transaction (transaction, service, owner))
703     {
704       BUS_SET_OOM (error);
705       return FALSE;
706     }
707   
708   bus_service_unlink_owner (service, owner);
709
710   if (service->owners == NULL)
711     bus_service_unlink (service);
712
713   return TRUE;
714 }
715
716 void
717 bus_service_ref (BusService *service)
718 {
719   _dbus_assert (service->refcount > 0);
720   
721   service->refcount += 1;
722 }
723
724 void
725 bus_service_unref (BusService *service)
726 {
727   _dbus_assert (service->refcount > 0);
728   
729   service->refcount -= 1;
730
731   if (service->refcount == 0)
732     {
733       _dbus_assert (service->owners == NULL);
734       
735       dbus_free (service->name);
736       _dbus_mem_pool_dealloc (service->registry->service_pool, service);
737     }
738 }
739
740 DBusConnection*
741 bus_service_get_primary_owner (BusService *service)
742 {
743   return _dbus_list_get_first (&service->owners);
744 }
745
746 const char*
747 bus_service_get_name (BusService *service)
748 {
749   return service->name;
750 }
751
752 void
753 bus_service_set_prohibit_replacement (BusService  *service,
754                                       dbus_bool_t  prohibit_replacement)
755 {
756   service->prohibit_replacement = prohibit_replacement != FALSE;
757 }
758
759 dbus_bool_t
760 bus_service_get_prohibit_replacement (BusService *service)
761 {
762   return service->prohibit_replacement;
763 }
764
765 dbus_bool_t
766 bus_service_has_owner (BusService     *service,
767                        DBusConnection *owner)
768 {
769   DBusList *link;
770
771   link = _dbus_list_get_first_link (&service->owners);
772   
773   while (link != NULL)
774     {
775       if (link->data == owner)
776         return TRUE;
777       
778       link = _dbus_list_get_next_link (&service->owners, link);
779     }
780
781   return FALSE;
782 }