2004-03-16 Richard Hult <richard@imendio.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 2.0
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 BusRegistry *
86 bus_registry_ref (BusRegistry *registry)
87 {
88   _dbus_assert (registry->refcount > 0);
89   registry->refcount += 1;
90
91   return registry;
92 }
93
94 void
95 bus_registry_unref  (BusRegistry *registry)
96 {
97   _dbus_assert (registry->refcount > 0);
98   registry->refcount -= 1;
99
100   if (registry->refcount == 0)
101     {
102       if (registry->service_hash)
103         _dbus_hash_table_unref (registry->service_hash);
104       if (registry->service_pool)
105         _dbus_mem_pool_free (registry->service_pool);
106
107       dbus_free (registry);
108     }
109 }
110
111 BusService*
112 bus_registry_lookup (BusRegistry      *registry,
113                      const DBusString *service_name)
114 {
115   BusService *service;
116
117   service = _dbus_hash_table_lookup_string (registry->service_hash,
118                                             _dbus_string_get_const_data (service_name));
119
120   return service;
121 }
122
123 BusService*
124 bus_registry_ensure (BusRegistry               *registry,
125                      const DBusString          *service_name,
126                      DBusConnection            *owner_if_created,
127                      BusTransaction            *transaction,
128                      DBusError                 *error)
129 {
130   BusService *service;
131
132   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
133   
134   _dbus_assert (owner_if_created != NULL);
135   _dbus_assert (transaction != NULL);
136
137   service = _dbus_hash_table_lookup_string (registry->service_hash,
138                                             _dbus_string_get_const_data (service_name));
139   if (service != NULL)
140     return service;
141   
142   service = _dbus_mem_pool_alloc (registry->service_pool);
143   if (service == NULL)
144     {
145       BUS_SET_OOM (error);
146       return NULL;
147     }
148
149   service->registry = registry;  
150   service->refcount = 1;
151   
152   if (!_dbus_string_copy_data (service_name, &service->name))
153     {
154       _dbus_mem_pool_dealloc (registry->service_pool, service);
155       BUS_SET_OOM (error);
156       return NULL;
157     }
158
159   if (!bus_driver_send_service_created (service->name, transaction, error))
160     {
161       bus_service_unref (service);
162       return NULL;
163     }
164
165   if (!bus_activation_service_created (bus_context_get_activation (registry->context),
166                                        service->name, transaction, error))
167     {
168       bus_service_unref (service);
169       return NULL;
170     }
171   
172   if (!bus_service_add_owner (service, owner_if_created,
173                               transaction, error))
174     {
175       bus_service_unref (service);
176       return NULL;
177     }
178   
179   if (!_dbus_hash_table_insert_string (registry->service_hash,
180                                        service->name,
181                                        service))
182     {
183       /* The add_owner gets reverted on transaction cancel */
184       BUS_SET_OOM (error);
185       return NULL;
186     }
187   
188   return service;
189 }
190
191 void
192 bus_registry_foreach (BusRegistry               *registry,
193                       BusServiceForeachFunction  function,
194                       void                      *data)
195 {
196   DBusHashIter iter;
197   
198   _dbus_hash_iter_init (registry->service_hash, &iter);
199   while (_dbus_hash_iter_next (&iter))
200     {
201       BusService *service = _dbus_hash_iter_get_value (&iter);
202
203       (* function) (service, data);
204     }
205 }
206
207 dbus_bool_t
208 bus_registry_list_services (BusRegistry *registry,
209                             char      ***listp,
210                             int         *array_len)
211 {
212   int i, j, len;
213   char **retval;
214   DBusHashIter iter;
215    
216   len = _dbus_hash_table_get_n_entries (registry->service_hash);
217   retval = dbus_new (char *, len + 1);
218
219   if (retval == NULL)
220     return FALSE;
221
222   _dbus_hash_iter_init (registry->service_hash, &iter);
223   i = 0;
224   while (_dbus_hash_iter_next (&iter))
225     {
226       BusService *service = _dbus_hash_iter_get_value (&iter);
227
228       retval[i] = _dbus_strdup (service->name);
229       if (retval[i] == NULL)
230         goto error;
231
232       i++;
233     }
234
235   retval[i] = NULL;
236   
237   if (array_len)
238     *array_len = len;
239   
240   *listp = retval;
241   return TRUE;
242   
243  error:
244   for (j = 0; j < i; j++)
245     dbus_free (retval[i]);
246   dbus_free (retval);
247
248   return FALSE;
249 }
250
251 dbus_bool_t
252 bus_registry_acquire_service (BusRegistry      *registry,
253                               DBusConnection   *connection,
254                               const DBusString *service_name,
255                               dbus_uint32_t     flags,
256                               dbus_uint32_t    *result,
257                               BusTransaction   *transaction,
258                               DBusError        *error)
259 {
260   dbus_bool_t retval;
261   DBusConnection *old_owner;
262   DBusConnection *current_owner;
263   BusClientPolicy *policy;
264   BusService *service;
265   BusActivation  *activation;
266   
267   retval = FALSE;
268
269   if (_dbus_string_get_length (service_name) == 0)
270     {
271       dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
272                       "Zero-length service name is not allowed");
273       
274       _dbus_verbose ("Attempt to acquire zero-length service name\n");
275       
276       goto out;
277     }
278   
279   if (_dbus_string_get_byte (service_name, 0) == ':')
280     {
281       /* Not allowed; only base services can start with ':' */
282       dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
283                       "Cannot acquire a service starting with ':' such as \"%s\"",
284                       _dbus_string_get_const_data (service_name));
285       
286       _dbus_verbose ("Attempt to acquire invalid base service name \"%s\"",
287                      _dbus_string_get_const_data (service_name));
288       
289       goto out;
290     }
291
292   policy = bus_connection_get_policy (connection);
293   _dbus_assert (policy != NULL);
294
295   if (!bus_client_policy_check_can_own (policy, connection,
296                                         service_name))
297     {
298       dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
299                       "Connection \"%s\" is not allowed to own the service \"%s\" due "
300                       "to security policies in the configuration file",
301                       bus_connection_is_active (connection) ?
302                       bus_connection_get_name (connection) :
303                       "(inactive)",
304                       service_name);
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   activation = bus_context_get_activation (registry->context);
381   retval = bus_activation_send_pending_auto_activation_messages (activation,
382                                                                  service,
383                                                                  transaction,
384                                                                  error);
385   
386  out:
387   return retval;
388 }
389
390 static void
391 bus_service_unlink_owner (BusService      *service,
392                           DBusConnection  *owner)
393 {
394   _dbus_list_remove_last (&service->owners, owner);
395   bus_connection_remove_owned_service (owner, service);
396 }
397
398 static void
399 bus_service_unlink (BusService *service)
400 {
401   _dbus_assert (service->owners == NULL);
402
403   /* the service may not be in the hash, if
404    * the failure causing transaction cancel
405    * was in the right place, but that's OK
406    */
407   _dbus_hash_table_remove_string (service->registry->service_hash,
408                                   service->name);
409   
410   bus_service_unref (service);
411 }
412
413 static void
414 bus_service_relink (BusService           *service,
415                     DBusPreallocatedHash *preallocated)
416 {
417   _dbus_assert (service->owners == NULL);
418   _dbus_assert (preallocated != NULL);
419
420   _dbus_hash_table_insert_string_preallocated (service->registry->service_hash,
421                                                preallocated,
422                                                service->name,
423                                                service);
424   
425   bus_service_ref (service);
426 }
427
428 /**
429  * Data used to represent an ownership cancellation in
430  * a bus transaction.
431  */
432 typedef struct
433 {
434   DBusConnection *connection; /**< the connection */
435   BusService *service;        /**< service to cancel ownership of */
436 } OwnershipCancelData;
437
438 static void
439 cancel_ownership (void *data)
440 {
441   OwnershipCancelData *d = data;
442
443   /* We don't need to send messages notifying of these
444    * changes, since we're reverting something that was
445    * cancelled (effectively never really happened)
446    */
447   bus_service_unlink_owner (d->service, d->connection);
448   
449   if (d->service->owners == NULL)
450     bus_service_unlink (d->service);
451 }
452
453 static void
454 free_ownership_cancel_data (void *data)
455 {
456   OwnershipCancelData *d = data;
457
458   dbus_connection_unref (d->connection);
459   bus_service_unref (d->service);
460   
461   dbus_free (d);
462 }
463
464 static dbus_bool_t
465 add_cancel_ownership_to_transaction (BusTransaction *transaction,
466                                      BusService     *service,
467                                      DBusConnection *connection)
468 {
469   OwnershipCancelData *d;
470
471   d = dbus_new (OwnershipCancelData, 1);
472   if (d == NULL)
473     return FALSE;
474   
475   d->service = service;
476   d->connection = connection;
477
478   if (!bus_transaction_add_cancel_hook (transaction, cancel_ownership, d,
479                                         free_ownership_cancel_data))
480     {
481       dbus_free (d);
482       return FALSE;
483     }
484
485   bus_service_ref (d->service);
486   dbus_connection_ref (d->connection);
487   
488   return TRUE;
489 }
490
491 /* this function is self-cancelling if you cancel the transaction */
492 dbus_bool_t
493 bus_service_add_owner (BusService     *service,
494                        DBusConnection *owner,
495                        BusTransaction *transaction,
496                        DBusError      *error)
497 {
498   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
499   
500  /* Send service acquired message first, OOM will result
501   * in cancelling the transaction
502   */
503   if (service->owners == NULL)
504     {
505       if (!bus_driver_send_service_acquired (owner, service->name, transaction, error))
506         return FALSE;
507     }
508   
509   if (!_dbus_list_append (&service->owners,
510                           owner))
511     {
512       BUS_SET_OOM (error);
513       return FALSE;
514     }
515
516   if (!bus_connection_add_owned_service (owner, service))
517     {
518       _dbus_list_remove_last (&service->owners, owner);
519       BUS_SET_OOM (error);
520       return FALSE;
521     }
522
523   if (!add_cancel_ownership_to_transaction (transaction,
524                                             service,
525                                             owner))
526     {
527       bus_service_unlink_owner (service, owner);
528       BUS_SET_OOM (error);
529       return FALSE;
530     }
531   
532   return TRUE;
533 }
534
535 typedef struct
536 {
537   DBusConnection *connection;
538   BusService     *service;
539   DBusConnection *before_connection; /* restore to position before this connection in owners list */
540   DBusList       *connection_link;
541   DBusList       *service_link;
542   DBusPreallocatedHash *hash_entry;
543 } OwnershipRestoreData;
544
545 static void
546 restore_ownership (void *data)
547 {
548   OwnershipRestoreData *d = data;
549   DBusList *link;
550
551   _dbus_assert (d->service_link != NULL);
552   _dbus_assert (d->connection_link != NULL);
553   
554   if (d->service->owners == NULL)
555     {
556       _dbus_assert (d->hash_entry != NULL);
557       bus_service_relink (d->service, d->hash_entry);
558     }
559   else
560     {
561       _dbus_assert (d->hash_entry == NULL);
562     }
563   
564   /* We don't need to send messages notifying of these
565    * changes, since we're reverting something that was
566    * cancelled (effectively never really happened)
567    */
568   link = _dbus_list_get_first_link (&d->service->owners);
569   while (link != NULL)
570     {
571       if (link->data == d->before_connection)
572         break;
573
574       link = _dbus_list_get_next_link (&d->service->owners, link);
575     }
576   
577   _dbus_list_insert_before_link (&d->service->owners, link, d->connection_link);
578
579   /* Note that removing then restoring this changes the order in which
580    * ServiceDeleted messages are sent on destruction of the
581    * connection.  This should be OK as the only guarantee there is
582    * that the base service is destroyed last, and we never even
583    * tentatively remove the base service.
584    */
585   bus_connection_add_owned_service_link (d->connection, d->service_link);
586   
587   d->hash_entry = NULL;
588   d->service_link = NULL;
589   d->connection_link = NULL;
590 }
591
592 static void
593 free_ownership_restore_data (void *data)
594 {
595   OwnershipRestoreData *d = data;
596
597   if (d->service_link)
598     _dbus_list_free_link (d->service_link);
599   if (d->connection_link)
600     _dbus_list_free_link (d->connection_link);
601   if (d->hash_entry)
602     _dbus_hash_table_free_preallocated_entry (d->service->registry->service_hash,
603                                               d->hash_entry);
604
605   dbus_connection_unref (d->connection);
606   bus_service_unref (d->service);
607   
608   dbus_free (d);
609 }
610
611 static dbus_bool_t
612 add_restore_ownership_to_transaction (BusTransaction *transaction,
613                                       BusService     *service,
614                                       DBusConnection *connection)
615 {
616   OwnershipRestoreData *d;
617   DBusList *link;
618
619   d = dbus_new (OwnershipRestoreData, 1);
620   if (d == NULL)
621     return FALSE;
622   
623   d->service = service;
624   d->connection = connection;
625   d->service_link = _dbus_list_alloc_link (service);
626   d->connection_link = _dbus_list_alloc_link (connection);
627   d->hash_entry = _dbus_hash_table_preallocate_entry (service->registry->service_hash);
628   
629   bus_service_ref (d->service);
630   dbus_connection_ref (d->connection);
631
632   d->before_connection = NULL;
633   link = _dbus_list_get_first_link (&service->owners);
634   while (link != NULL)
635     {
636       if (link->data == connection)
637         {
638           link = _dbus_list_get_next_link (&service->owners, link);
639
640           if (link)
641             d->before_connection = link->data;
642
643           break;
644         }
645       
646       link = _dbus_list_get_next_link (&service->owners, link);
647     }
648   
649   if (d->service_link == NULL ||
650       d->connection_link == NULL ||
651       d->hash_entry == NULL ||
652       !bus_transaction_add_cancel_hook (transaction, restore_ownership, d,
653                                         free_ownership_restore_data))
654     {
655       free_ownership_restore_data (d);
656       return FALSE;
657     }
658   
659   return TRUE;
660 }
661
662 /* this function is self-cancelling if you cancel the transaction */
663 dbus_bool_t
664 bus_service_remove_owner (BusService     *service,
665                           DBusConnection *owner,
666                           BusTransaction *transaction,
667                           DBusError      *error)
668 {
669   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
670   
671   /* We send out notifications before we do any work we
672    * might have to undo if the notification-sending failed
673    */
674   
675   /* Send service lost message */
676   if (bus_service_get_primary_owner (service) == owner)
677     {
678       if (!bus_driver_send_service_lost (owner, service->name,
679                                          transaction, error))
680         return FALSE;
681     }
682
683   if (service->owners == NULL)
684     {
685       _dbus_assert_not_reached ("Tried to remove owner of a service that has no owners");
686     }
687   else if (_dbus_list_length_is_one (&service->owners))
688     {
689       if (!bus_driver_send_service_deleted (service->name,
690                                             transaction, error))
691         return FALSE;
692     }
693   else
694     {
695       DBusList *link;
696       link = _dbus_list_get_first (&service->owners);
697       _dbus_assert (link != NULL);
698       link = _dbus_list_get_next_link (&service->owners, link);
699
700       _dbus_assert (link != NULL);
701
702       /* This will be our new owner */
703       if (!bus_driver_send_service_acquired (link->data,
704                                              service->name,
705                                              transaction,
706                                              error))
707         return FALSE;
708     }
709
710   if (!add_restore_ownership_to_transaction (transaction, service, owner))
711     {
712       BUS_SET_OOM (error);
713       return FALSE;
714     }
715   
716   bus_service_unlink_owner (service, owner);
717
718   if (service->owners == NULL)
719     bus_service_unlink (service);
720
721   return TRUE;
722 }
723
724 BusService *
725 bus_service_ref (BusService *service)
726 {
727   _dbus_assert (service->refcount > 0);
728   
729   service->refcount += 1;
730
731   return service;
732 }
733
734 void
735 bus_service_unref (BusService *service)
736 {
737   _dbus_assert (service->refcount > 0);
738   
739   service->refcount -= 1;
740
741   if (service->refcount == 0)
742     {
743       _dbus_assert (service->owners == NULL);
744       
745       dbus_free (service->name);
746       _dbus_mem_pool_dealloc (service->registry->service_pool, service);
747     }
748 }
749
750 DBusConnection*
751 bus_service_get_primary_owner (BusService *service)
752 {
753   return _dbus_list_get_first (&service->owners);
754 }
755
756 const char*
757 bus_service_get_name (BusService *service)
758 {
759   return service->name;
760 }
761
762 void
763 bus_service_set_prohibit_replacement (BusService  *service,
764                                       dbus_bool_t  prohibit_replacement)
765 {
766   service->prohibit_replacement = prohibit_replacement != FALSE;
767 }
768
769 dbus_bool_t
770 bus_service_get_prohibit_replacement (BusService *service)
771 {
772   return service->prohibit_replacement;
773 }
774
775 dbus_bool_t
776 bus_service_has_owner (BusService     *service,
777                        DBusConnection *owner)
778 {
779   DBusList *link;
780
781   link = _dbus_list_get_first_link (&service->owners);
782   
783   while (link != NULL)
784     {
785       if (link->data == owner)
786         return TRUE;
787       
788       link = _dbus_list_get_next_link (&service->owners, link);
789     }
790
791   return FALSE;
792 }