5148f1f1a56e951fa87c0ce99fe143ef20b43ddd
[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);
290   _dbus_assert (policy != NULL);
291
292   if (!bus_client_policy_check_can_own (policy, connection,
293                                         service_name))
294     {
295       dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
296                       "Connection \"%s\" is not allowed to own the service \"%s\" due "
297                       "to security policies in the configuration file",
298                       bus_connection_is_active (connection) ?
299                       bus_connection_get_name (connection) :
300                       "(inactive)");
301       goto out;
302     }
303
304   if (bus_connection_get_n_services_owned (connection) >=
305       bus_context_get_max_services_per_connection (registry->context))
306     {
307       dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
308                       "Connection \"%s\" is not allowed to own more services "
309                       "(increase limits in configuration file if required)",
310                       bus_connection_is_active (connection) ?
311                       bus_connection_get_name (connection) :
312                       "(inactive)");
313       goto out;
314     }
315   
316   service = bus_registry_lookup (registry, service_name);
317
318   if (service != NULL)
319     old_owner = bus_service_get_primary_owner (service);
320   else
321     old_owner = NULL;
322       
323   if (service == NULL)
324     {
325       service = bus_registry_ensure (registry,
326                                      service_name, connection, transaction, error);
327       if (service == NULL)
328         goto out;
329     }
330
331   current_owner = bus_service_get_primary_owner (service);
332
333   if (old_owner == NULL)
334     {
335       _dbus_assert (current_owner == connection);
336
337       bus_service_set_prohibit_replacement (service,
338                                             (flags & DBUS_SERVICE_FLAG_PROHIBIT_REPLACEMENT));      
339                         
340       *result = DBUS_SERVICE_REPLY_PRIMARY_OWNER;      
341     }
342   else if (old_owner == connection)
343     *result = DBUS_SERVICE_REPLY_ALREADY_OWNER;
344   else if (!((flags & DBUS_SERVICE_FLAG_REPLACE_EXISTING)))
345     *result = DBUS_SERVICE_REPLY_SERVICE_EXISTS;
346   else if (bus_service_get_prohibit_replacement (service))
347     {
348       /* Queue the connection */
349       if (!bus_service_add_owner (service, connection,
350                                   transaction, error))
351         goto out;
352       
353       *result = DBUS_SERVICE_REPLY_IN_QUEUE;
354     }
355   else
356     {
357       /* Replace the current owner */
358
359       /* We enqueue the new owner and remove the first one because
360        * that will cause ServiceAcquired and ServiceLost messages to
361        * be sent.
362        */
363       
364       if (!bus_service_add_owner (service, connection,
365                                   transaction, error))
366         goto out;
367
368       if (!bus_service_remove_owner (service, old_owner,
369                                      transaction, error))
370         goto out;
371       
372       _dbus_assert (connection == bus_service_get_primary_owner (service));
373       *result = DBUS_SERVICE_REPLY_PRIMARY_OWNER;
374     }
375
376   retval = TRUE;
377   
378  out:
379   return retval;
380 }
381
382 static void
383 bus_service_unlink_owner (BusService      *service,
384                           DBusConnection  *owner)
385 {
386   _dbus_list_remove_last (&service->owners, owner);
387   bus_connection_remove_owned_service (owner, service);
388 }
389
390 static void
391 bus_service_unlink (BusService *service)
392 {
393   _dbus_assert (service->owners == NULL);
394
395   /* the service may not be in the hash, if
396    * the failure causing transaction cancel
397    * was in the right place, but that's OK
398    */
399   _dbus_hash_table_remove_string (service->registry->service_hash,
400                                   service->name);
401   
402   bus_service_unref (service);
403 }
404
405 static void
406 bus_service_relink (BusService           *service,
407                     DBusPreallocatedHash *preallocated)
408 {
409   _dbus_assert (service->owners == NULL);
410   _dbus_assert (preallocated != NULL);
411
412   _dbus_hash_table_insert_string_preallocated (service->registry->service_hash,
413                                                preallocated,
414                                                service->name,
415                                                service);
416   
417   bus_service_ref (service);
418 }
419
420 typedef struct
421 {
422   DBusConnection *connection;
423   BusService *service;
424 } OwnershipCancelData;
425
426 static void
427 cancel_ownership (void *data)
428 {
429   OwnershipCancelData *d = data;
430
431   /* We don't need to send messages notifying of these
432    * changes, since we're reverting something that was
433    * cancelled (effectively never really happened)
434    */
435   bus_service_unlink_owner (d->service, d->connection);
436   
437   if (d->service->owners == NULL)
438     bus_service_unlink (d->service);
439 }
440
441 static void
442 free_ownership_cancel_data (void *data)
443 {
444   OwnershipCancelData *d = data;
445
446   dbus_connection_unref (d->connection);
447   bus_service_unref (d->service);
448   
449   dbus_free (d);
450 }
451
452 static dbus_bool_t
453 add_cancel_ownership_to_transaction (BusTransaction *transaction,
454                                      BusService     *service,
455                                      DBusConnection *connection)
456 {
457   OwnershipCancelData *d;
458
459   d = dbus_new (OwnershipCancelData, 1);
460   if (d == NULL)
461     return FALSE;
462   
463   d->service = service;
464   d->connection = connection;
465
466   if (!bus_transaction_add_cancel_hook (transaction, cancel_ownership, d,
467                                         free_ownership_cancel_data))
468     {
469       dbus_free (d);
470       return FALSE;
471     }
472
473   bus_service_ref (d->service);
474   dbus_connection_ref (d->connection);
475   
476   return TRUE;
477 }
478
479 /* this function is self-cancelling if you cancel the transaction */
480 dbus_bool_t
481 bus_service_add_owner (BusService     *service,
482                        DBusConnection *owner,
483                        BusTransaction *transaction,
484                        DBusError      *error)
485 {
486   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
487   
488  /* Send service acquired message first, OOM will result
489   * in cancelling the transaction
490   */
491   if (service->owners == NULL)
492     {
493       if (!bus_driver_send_service_acquired (owner, service->name, transaction, error))
494         return FALSE;
495     }
496   
497   if (!_dbus_list_append (&service->owners,
498                           owner))
499     {
500       BUS_SET_OOM (error);
501       return FALSE;
502     }
503
504   if (!bus_connection_add_owned_service (owner, service))
505     {
506       _dbus_list_remove_last (&service->owners, owner);
507       BUS_SET_OOM (error);
508       return FALSE;
509     }
510
511   if (!add_cancel_ownership_to_transaction (transaction,
512                                             service,
513                                             owner))
514     {
515       bus_service_unlink_owner (service, owner);
516       BUS_SET_OOM (error);
517       return FALSE;
518     }
519   
520   return TRUE;
521 }
522
523 typedef struct
524 {
525   DBusConnection *connection;
526   BusService     *service;
527   DBusConnection *before_connection; /* restore to position before this connection in owners list */
528   DBusList       *connection_link;
529   DBusList       *service_link;
530   DBusPreallocatedHash *hash_entry;
531 } OwnershipRestoreData;
532
533 static void
534 restore_ownership (void *data)
535 {
536   OwnershipRestoreData *d = data;
537   DBusList *link;
538
539   _dbus_assert (d->service_link != NULL);
540   _dbus_assert (d->connection_link != NULL);
541   
542   if (d->service->owners == NULL)
543     {
544       _dbus_assert (d->hash_entry != NULL);
545       bus_service_relink (d->service, d->hash_entry);
546     }
547   else
548     {
549       _dbus_assert (d->hash_entry == NULL);
550     }
551   
552   /* We don't need to send messages notifying of these
553    * changes, since we're reverting something that was
554    * cancelled (effectively never really happened)
555    */
556   link = _dbus_list_get_first_link (&d->service->owners);
557   while (link != NULL)
558     {
559       if (link->data == d->before_connection)
560         break;
561
562       link = _dbus_list_get_next_link (&d->service->owners, link);
563     }
564   
565   _dbus_list_insert_before_link (&d->service->owners, link, d->connection_link);
566
567   /* Note that removing then restoring this changes the order in which
568    * ServiceDeleted messages are sent on destruction of the
569    * connection.  This should be OK as the only guarantee there is
570    * that the base service is destroyed last, and we never even
571    * tentatively remove the base service.
572    */
573   bus_connection_add_owned_service_link (d->connection, d->service_link);
574   
575   d->hash_entry = NULL;
576   d->service_link = NULL;
577   d->connection_link = NULL;
578 }
579
580 static void
581 free_ownership_restore_data (void *data)
582 {
583   OwnershipRestoreData *d = data;
584
585   if (d->service_link)
586     _dbus_list_free_link (d->service_link);
587   if (d->connection_link)
588     _dbus_list_free_link (d->connection_link);
589   if (d->hash_entry)
590     _dbus_hash_table_free_preallocated_entry (d->service->registry->service_hash,
591                                               d->hash_entry);
592
593   dbus_connection_unref (d->connection);
594   bus_service_unref (d->service);
595   
596   dbus_free (d);
597 }
598
599 static dbus_bool_t
600 add_restore_ownership_to_transaction (BusTransaction *transaction,
601                                       BusService     *service,
602                                       DBusConnection *connection)
603 {
604   OwnershipRestoreData *d;
605   DBusList *link;
606
607   d = dbus_new (OwnershipRestoreData, 1);
608   if (d == NULL)
609     return FALSE;
610   
611   d->service = service;
612   d->connection = connection;
613   d->service_link = _dbus_list_alloc_link (service);
614   d->connection_link = _dbus_list_alloc_link (connection);
615   d->hash_entry = _dbus_hash_table_preallocate_entry (service->registry->service_hash);
616   
617   bus_service_ref (d->service);
618   dbus_connection_ref (d->connection);
619
620   d->before_connection = NULL;
621   link = _dbus_list_get_first_link (&service->owners);
622   while (link != NULL)
623     {
624       if (link->data == connection)
625         {
626           link = _dbus_list_get_next_link (&service->owners, link);
627
628           if (link)
629             d->before_connection = link->data;
630
631           break;
632         }
633       
634       link = _dbus_list_get_next_link (&service->owners, link);
635     }
636   
637   if (d->service_link == NULL ||
638       d->connection_link == NULL ||
639       d->hash_entry == NULL ||
640       !bus_transaction_add_cancel_hook (transaction, restore_ownership, d,
641                                         free_ownership_restore_data))
642     {
643       free_ownership_restore_data (d);
644       return FALSE;
645     }
646   
647   return TRUE;
648 }
649
650 /* this function is self-cancelling if you cancel the transaction */
651 dbus_bool_t
652 bus_service_remove_owner (BusService     *service,
653                           DBusConnection *owner,
654                           BusTransaction *transaction,
655                           DBusError      *error)
656 {
657   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
658   
659   /* We send out notifications before we do any work we
660    * might have to undo if the notification-sending failed
661    */
662   
663   /* Send service lost message */
664   if (bus_service_get_primary_owner (service) == owner)
665     {
666       if (!bus_driver_send_service_lost (owner, service->name,
667                                          transaction, error))
668         return FALSE;
669     }
670
671   if (service->owners == NULL)
672     {
673       _dbus_assert_not_reached ("Tried to remove owner of a service that has no owners");
674     }
675   else if (_dbus_list_length_is_one (&service->owners))
676     {
677       if (!bus_driver_send_service_deleted (service->name,
678                                             transaction, error))
679         return FALSE;
680     }
681   else
682     {
683       DBusList *link;
684       link = _dbus_list_get_first (&service->owners);
685       _dbus_assert (link != NULL);
686       link = _dbus_list_get_next_link (&service->owners, link);
687
688       _dbus_assert (link != NULL);
689
690       /* This will be our new owner */
691       if (!bus_driver_send_service_acquired (link->data,
692                                              service->name,
693                                              transaction,
694                                              error))
695         return FALSE;
696     }
697
698   if (!add_restore_ownership_to_transaction (transaction, service, owner))
699     {
700       BUS_SET_OOM (error);
701       return FALSE;
702     }
703   
704   bus_service_unlink_owner (service, owner);
705
706   if (service->owners == NULL)
707     bus_service_unlink (service);
708
709   return TRUE;
710 }
711
712 void
713 bus_service_ref (BusService *service)
714 {
715   _dbus_assert (service->refcount > 0);
716   
717   service->refcount += 1;
718 }
719
720 void
721 bus_service_unref (BusService *service)
722 {
723   _dbus_assert (service->refcount > 0);
724   
725   service->refcount -= 1;
726
727   if (service->refcount == 0)
728     {
729       _dbus_assert (service->owners == NULL);
730       
731       dbus_free (service->name);
732       _dbus_mem_pool_dealloc (service->registry->service_pool, service);
733     }
734 }
735
736 DBusConnection*
737 bus_service_get_primary_owner (BusService *service)
738 {
739   return _dbus_list_get_first (&service->owners);
740 }
741
742 const char*
743 bus_service_get_name (BusService *service)
744 {
745   return service->name;
746 }
747
748 void
749 bus_service_set_prohibit_replacement (BusService  *service,
750                                       dbus_bool_t  prohibit_replacement)
751 {
752   service->prohibit_replacement = prohibit_replacement != FALSE;
753 }
754
755 dbus_bool_t
756 bus_service_get_prohibit_replacement (BusService *service)
757 {
758   return service->prohibit_replacement;
759 }
760
761 dbus_bool_t
762 bus_service_has_owner (BusService     *service,
763                        DBusConnection *owner)
764 {
765   DBusList *link;
766
767   link = _dbus_list_get_first_link (&service->owners);
768   
769   while (link != NULL)
770     {
771       if (link->data == owner)
772         return TRUE;
773       
774       link = _dbus_list_get_next_link (&service->owners, link);
775     }
776
777   return FALSE;
778 }